diff --git a/go/types/assignments.go b/go/types/assignments.go index 569d46fe..2f44dede 100644 --- a/go/types/assignments.go +++ b/go/types/assignments.go @@ -8,6 +8,7 @@ package types import ( "go/ast" + "go/token" "code.google.com/p/go.tools/go/exact" ) @@ -154,7 +155,9 @@ func (check *checker) assignVar(lhs ast.Expr, x *operand) Type { return z.typ } -func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) { +// If returnPos is valid, initVars is called to type-check the assignment of +// return expressions, and returnPos is the position of the return statement. +func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos) { l := len(lhs) r := len(rhs) assert(l > 0) @@ -199,8 +202,8 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) { } } - if allowCommaOk && x.mode == valueok && l == 2 { - // comma-ok expression + if !returnPos.IsValid() && x.mode == valueok && l == 2 { + // comma-ok expression (not permitted with return statements) x.mode = value t1 := check.initVar(lhs[0], &x) @@ -216,10 +219,15 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) { } } + invalidateVars(lhs) + // lhs variables may be function result parameters (return statement); // use rhs position for properly located error messages + if returnPos.IsValid() { + check.errorf(returnPos, "wrong number of return values (want %d, got %d)", l, r) + return + } check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r) - invalidateVars(lhs) } func (check *checker) assignVars(lhs, rhs []ast.Expr) { @@ -318,7 +326,7 @@ func (check *checker) shortVarDecl(lhs, rhs []ast.Expr) { vars[i] = obj } - check.initVars(vars, rhs, true) + check.initVars(vars, rhs, token.NoPos) // declare variables n := scope.Len() diff --git a/go/types/expr.go b/go/types/expr.go index e71b2d96..8a8151fc 100644 --- a/go/types/expr.go +++ b/go/types/expr.go @@ -768,14 +768,14 @@ func (check *checker) index(arg ast.Expr, length int64) (i int64, ok bool) { // the index/size must be of integer type if !isInteger(x.typ) { - check.invalidArg(x.pos(), "%s must be integer", &x) + check.invalidArg(x.pos(), "index %s must be integer", &x) return } // a constant index/size i must be 0 <= i < length if x.mode == constant { if exact.Sign(x.val) < 0 { - check.invalidArg(x.pos(), "%s must not be negative", &x) + check.invalidArg(x.pos(), "index %s must not be negative", &x) return } i, ok = exact.Int64Val(x.val) @@ -806,8 +806,10 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64) int64 if i, ok := check.index(kv.Key, length); ok { if i >= 0 { index = i + validIndex = true + } else { + check.errorf(e.Pos(), "index %s must be integer constant", kv.Key) } - validIndex = true } eval = kv.Value } else if length >= 0 && index >= length { diff --git a/go/types/resolver.go b/go/types/resolver.go index 2832ae03..0c0ea5cc 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -473,7 +473,7 @@ func (check *checker) varDecl(obj *Var, typ, init ast.Expr) { } if m, _ := init.(*multiExpr); m != nil { - check.initVars(m.lhs, m.rhs, true) + check.initVars(m.lhs, m.rhs, token.NoPos) return } diff --git a/go/types/stdlib_test.go b/go/types/stdlib_test.go index b84d00d0..e971d08d 100644 --- a/go/types/stdlib_test.go +++ b/go/types/stdlib_test.go @@ -112,12 +112,10 @@ func TestStdfixed(t *testing.T) { "bug136.go", "bug179.go", "bug344.go", // TODO(gri) implement missing label checks "bug251.go", // TODO(gri) incorrect cycle checks for interface types "bug165.go", // TODO(gri) isComparable not working for incomplete struct type - "bug176.go", // TODO(gri) composite literal array index must be non-negative constant "bug200.go", // TODO(gri) complete duplicate checking in expr switches "bug223.go", "bug413.go", "bug459.go", // TODO(gri) complete initialization checks "bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore "bug250.go", // TODO(gri) fix recursive interfaces - "bug326.go", // TODO(gri) assignment doesn't guard against len(rhs) == 0 "bug373.go", // TODO(gri) implement use checks "bug376.go", // TODO(gri) built-ins must be called (no built-in function expressions) "issue3924.go", // TODO(gri) && and || produce bool result (not untyped bool) diff --git a/go/types/stmt.go b/go/types/stmt.go index b0415c01..8b097801 100644 --- a/go/types/stmt.go +++ b/go/types/stmt.go @@ -230,7 +230,7 @@ func (check *checker) stmt(s ast.Stmt, fallthroughOk bool) { lhs[i] = res } if len(s.Results) > 0 || !named { - check.initVars(lhs, s.Results, false) + check.initVars(lhs, s.Results, s.Return) return } } else if len(s.Results) > 0 { diff --git a/go/types/testdata/expr3.src b/go/types/testdata/expr3.src index 4eca42b2..a91c7ea7 100644 --- a/go/types/testdata/expr3.src +++ b/go/types/testdata/expr3.src @@ -178,6 +178,14 @@ func array_literals() { _ = A1{2.1 /* ERROR "overflows" */ } _ = A1{"foo" /* ERROR "cannot convert" */ } + // indices must be integer constants + i := 1 + const f = 2.1 + const s = "foo" + _ = A1{i /* ERROR "index i must be integer constant" */ : 0} + _ = A1{f /* ERROR "cannot be .* represented" */ : 0} + _ = A1{s /* ERROR "cannot convert" */ : 0} + a0 := [...]int{} assert(len(a0) == 0) @@ -226,14 +234,22 @@ func slice_literals() { _ = S0{"foo" /* ERROR "cannot convert" */ } // indices must be resolved correctly - // (for details, see comment in go/parser/parser.go, method parseElement) - index1 := 1 + const index1 = 1 _ = S0{index1: 1} _ = S0{index2: 2} _ = S0{index3 /* ERROR "undeclared name" */ : 3} + + // indices must be integer constants + i := 1 + const f = 2.1 + const s = "foo" + _ = S0{i /* ERROR "index i must be integer constant" */ : 0} + _ = S0{f /* ERROR "cannot be .* represented" */ : 0} + _ = S0{s /* ERROR "cannot convert" */ : 0} + } -var index2 int = 2 +const index2 int = 2 func map_literals() { type M0 map[string]int @@ -247,14 +263,14 @@ func map_literals() { _ = M0{"foo": 1, "bar": 2, "foo" /* ERROR "duplicate key" */ : 3 } // map keys must be resolved correctly - // (for details, see comment in go/parser/parser.go, method parseElement) key1 := "foo" _ = M0{key1: 1} _ = M0{key2: 2} _ = M0{key3 /* ERROR "undeclared name" */ : 2} + var value int _ = M1{true: 1, false: 0} - _ = M2{nil: 0, &index2: 1} + _ = M2{nil: 0, &value: 1} } var key2 string = "bar" diff --git a/go/types/testdata/stmt0.src b/go/types/testdata/stmt0.src index 24b54b97..5317fc85 100644 --- a/go/types/testdata/stmt0.src +++ b/go/types/testdata/stmt0.src @@ -6,7 +6,35 @@ package stmt0 -func assignments() { +func assignments0() (int, int) { + var a, b, c int + var ch chan int + f0 := func() {} + f1 := func() int { return 1 } + f2 := func() (int, int) { return 1, 2 } + f3 := func() (int, int, int) { return 1, 2, 3 } + + a, b, c = 1, 2, 3 + a, b, c = 1 /* ERROR "assignment count mismatch" */ , 2 + a, b, c = 1 /* ERROR "assignment count mismatch" */ , 2, 3, 4 + + a = f0 /* ERROR "used as value" */ () + a = f1() + a = f2 /* ERROR "used as single value" */ () + a, b = f2() + a, b, c = f2 /* ERROR "assignment count mismatch" */ () + a, b, c = f3() + a, b = f3 /* ERROR "assignment count mismatch" */ () + + a, b, c = <- /* ERROR "assignment count mismatch" */ ch + + return /* ERROR "wrong number of return values" */ + return /* ERROR "wrong number of return values" */ 1 + return 1, 2 + return /* ERROR "wrong number of return values" */ 1, 2, 3 +} + +func assignments1() { b, i, f, c, s := false, 1, 1.0, 1i, "foo" b = i /* ERROR "cannot assign" */ i = f /* ERROR "cannot assign" */ @@ -49,7 +77,7 @@ func assignments() { // test cases for issue 5500 _ = func() (int, bool) { var m map[int]int - return m /* ERROR "assignment count mismatch" */ [0] + return /* ERROR "wrong number of return values" */ m[0] } g := func(int, bool){}