go/types: typecheck loop body even if range clause is broken

Be more tolerant in the presence of incorrect range clauses
if for loops and type-check loop body with minimal assumptions
about iteration variables. (Before, in some cases we would simply
ignore the loop body in such cases).

Fixes #10148.

Change-Id: I0b66f81875348088c1a7fa04ccdcbfe768f2eb6c
Reviewed-on: https://go-review.googlesource.com/7525
Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
Robert Griesemer 2015-03-13 14:35:38 -07:00
parent 868c429971
commit e4a1c78f0f
2 changed files with 63 additions and 52 deletions

View File

@ -611,20 +611,12 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
defer check.closeScope() defer check.closeScope()
// check expression to iterate over // check expression to iterate over
decl := s.Tok == token.DEFINE
var x operand var x operand
check.expr(&x, s.X) check.expr(&x, s.X)
if x.mode == invalid {
// if we don't have a declaration, we can still check the loop's body
// (otherwise we can't because we are missing the declared variables)
if !decl {
check.stmt(inner, s.Body)
}
return
}
// determine key/value types // determine key/value types
var key, val Type var key, val Type
if x.mode != invalid {
switch typ := x.typ.Underlying().(type) { switch typ := x.typ.Underlying().(type) {
case *Basic: case *Basic:
if isString(typ) { if isString(typ) {
@ -657,14 +649,11 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
// ok to continue // ok to continue
} }
} }
}
if key == nil { if key == nil {
check.errorf(x.pos(), "cannot range over %s", &x) check.errorf(x.pos(), "cannot range over %s", &x)
// if we don't have a declaration, we can still check the loop's body // ok to continue
if !decl {
check.stmt(inner, s.Body)
}
return
} }
// check assignment to/declaration of iteration variables // check assignment to/declaration of iteration variables
@ -672,9 +661,9 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
// lhs expressions and initialization value (rhs) types // lhs expressions and initialization value (rhs) types
lhs := [2]ast.Expr{s.Key, s.Value} lhs := [2]ast.Expr{s.Key, s.Value}
rhs := [2]Type{key, val} rhs := [2]Type{key, val} // key, val may be nil
if decl { if s.Tok == token.DEFINE {
// short variable declaration; variable scope starts after the range clause // short variable declaration; variable scope starts after the range clause
// (the for loop opens a new scope, so variables on the lhs never redeclare // (the for loop opens a new scope, so variables on the lhs never redeclare
// previously declared variables) // previously declared variables)
@ -701,10 +690,15 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
} }
// initialize lhs variable // initialize lhs variable
if typ := rhs[i]; typ != nil {
x.mode = value x.mode = value
x.expr = lhs // we don't have a better rhs expression to use here x.expr = lhs // we don't have a better rhs expression to use here
x.typ = rhs[i] x.typ = typ
check.initVar(obj, &x, false) check.initVar(obj, &x, false)
} else {
obj.typ = Typ[Invalid]
obj.used = true // don't complain about unused variable
}
} }
// declare variables // declare variables
@ -721,12 +715,14 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
if lhs == nil { if lhs == nil {
continue continue
} }
if typ := rhs[i]; typ != nil {
x.mode = value x.mode = value
x.expr = lhs // we don't have a better rhs expression to use here x.expr = lhs // we don't have a better rhs expression to use here
x.typ = rhs[i] x.typ = typ
check.assignVar(lhs, &x) check.assignVar(lhs, &x)
} }
} }
}
check.stmt(inner, s.Body) check.stmt(inner, s.Body)

View File

@ -785,6 +785,21 @@ func issue6766b() {
for a, a /* ERROR redeclared */ := range []int{1, 2, 3} { _ = a } for a, a /* ERROR redeclared */ := range []int{1, 2, 3} { _ = a }
} }
// Test that despite errors in the range clause,
// the loop body is still type-checked (and thus
// errors reported).
func issue10148() {
for y /* ERROR declared but not used */ := range "" {
_ = "" /* ERROR cannot convert */ + 1
}
for range 1 /* ERROR cannot range over 1 */ {
_ = "" /* ERROR cannot convert */ + 1
}
for y := range 1 /* ERROR cannot range over 1 */ {
_ = "" /* ERROR cannot convert */ + 1
}
}
func labels0() { func labels0() {
goto L0 goto L0
goto L1 goto L1