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,60 +611,49 @@ 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
switch typ := x.typ.Underlying().(type) { if x.mode != invalid {
case *Basic: switch typ := x.typ.Underlying().(type) {
if isString(typ) { case *Basic:
key = Typ[Int] if isString(typ) {
val = UniverseRune // use 'rune' name key = Typ[Int]
} val = UniverseRune // use 'rune' name
case *Array: }
key = Typ[Int] case *Array:
val = typ.elem
case *Slice:
key = Typ[Int]
val = typ.elem
case *Pointer:
if typ, _ := typ.base.Underlying().(*Array); typ != nil {
key = Typ[Int] key = Typ[Int]
val = typ.elem val = typ.elem
} case *Slice:
case *Map: key = Typ[Int]
key = typ.key val = typ.elem
val = typ.elem case *Pointer:
case *Chan: if typ, _ := typ.base.Underlying().(*Array); typ != nil {
key = typ.elem key = Typ[Int]
val = Typ[Invalid] val = typ.elem
if typ.dir == SendOnly { }
check.errorf(x.pos(), "cannot range over send-only channel %s", &x) case *Map:
// ok to continue key = typ.key
} val = typ.elem
if s.Value != nil { case *Chan:
check.errorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x) key = typ.elem
// ok to continue val = Typ[Invalid]
if typ.dir == SendOnly {
check.errorf(x.pos(), "cannot range over send-only channel %s", &x)
// ok to continue
}
if s.Value != nil {
check.errorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x)
// 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
x.mode = value if typ := rhs[i]; typ != nil {
x.expr = lhs // we don't have a better rhs expression to use here x.mode = value
x.typ = rhs[i] x.expr = lhs // we don't have a better rhs expression to use here
check.initVar(obj, &x, false) x.typ = typ
check.initVar(obj, &x, false)
} else {
obj.typ = Typ[Invalid]
obj.used = true // don't complain about unused variable
}
} }
// declare variables // declare variables
@ -721,10 +715,12 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
if lhs == nil { if lhs == nil {
continue continue
} }
x.mode = value if typ := rhs[i]; typ != nil {
x.expr = lhs // we don't have a better rhs expression to use here x.mode = value
x.typ = rhs[i] x.expr = lhs // we don't have a better rhs expression to use here
check.assignVar(lhs, &x) x.typ = typ
check.assignVar(lhs, &x)
}
} }
} }

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