diff --git a/go/types/assignments.go b/go/types/assignments.go index 8af3366f92..4d5b59c6f5 100644 --- a/go/types/assignments.go +++ b/go/types/assignments.go @@ -24,7 +24,7 @@ func (check *checker) assignment(x *operand, to Type) bool { } if t, ok := x.typ.(*Tuple); ok { - // TODO(gri) elsewhere we use "assignment count mismatch" (consolidate) + assert(t.Len() > 1) check.errorf(x.pos(), "%d-valued expression %s used as single value", t.Len(), x) x.mode = invalid return false @@ -136,11 +136,13 @@ func (check *checker) assignVar(lhs ast.Expr, x *operand) { } func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) { - assert(len(lhs) > 0) + l := len(lhs) + r := len(rhs) + assert(l > 0) // If the lhs and rhs have corresponding expressions, // treat each matching pair as an individual pair. - if len(lhs) == len(rhs) { + if l == r { var x operand for i, e := range rhs { check.expr(&x, e) @@ -152,8 +154,8 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) { // Otherwise, the rhs must be a single expression (possibly // a function call returning multiple values, or a comma-ok // expression). - if len(rhs) == 1 { - // len(lhs) > 1 + if r == 1 { + // l > 1 // Start with rhs so we have expression types // for declarations with implicit types. var x operand @@ -163,18 +165,21 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) { return } - if t, ok := x.typ.(*Tuple); ok && len(lhs) == t.Len() { + if t, ok := x.typ.(*Tuple); ok { // function result - for i, lhs := range lhs { - x.mode = value - x.expr = rhs[0] - x.typ = t.At(i).typ - check.initVar(lhs, &x) + r = t.Len() + if l == r { + for i, lhs := range lhs { + x.mode = value + x.expr = rhs[0] + x.typ = t.At(i).typ + check.initVar(lhs, &x) + } + return } - return } - if allowCommaOk && x.mode == valueok && len(lhs) == 2 { + if allowCommaOk && x.mode == valueok && l == 2 { // comma-ok expression x.mode = value check.initVar(lhs[0], &x) @@ -186,18 +191,20 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) { } } - // lhs variables may be function parameters (return assignment); - // use rhs position information for properly located error messages - check.errorf(rhs[0].Pos(), "assignment count mismatch: %d = %d", len(lhs), len(rhs)) + // lhs variables may be function result parameters (return statement); + // use rhs position for properly located error messages + check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r) invalidateVars(lhs) } func (check *checker) assignVars(lhs, rhs []ast.Expr) { - assert(len(lhs) > 0) + l := len(lhs) + r := len(rhs) + assert(l > 0) // If the lhs and rhs have corresponding expressions, // treat each matching pair as an individual pair. - if len(lhs) == len(rhs) { + if l == r { var x operand for i, e := range rhs { check.expr(&x, e) @@ -209,26 +216,29 @@ func (check *checker) assignVars(lhs, rhs []ast.Expr) { // Otherwise, the rhs must be a single expression (possibly // a function call returning multiple values, or a comma-ok // expression). - if len(rhs) == 1 { - // len(lhs) > 1 + if r == 1 { + // l > 1 var x operand check.expr(&x, rhs[0]) if x.mode == invalid { return } - if t, ok := x.typ.(*Tuple); ok && len(lhs) == t.Len() { + if t, ok := x.typ.(*Tuple); ok { // function result - for i, lhs := range lhs { - x.mode = value - x.expr = rhs[0] - x.typ = t.At(i).typ - check.assignVar(lhs, &x) + r = t.Len() + if l == r { + for i, lhs := range lhs { + x.mode = value + x.expr = rhs[0] + x.typ = t.At(i).typ + check.assignVar(lhs, &x) + } + return } - return } - if x.mode == valueok && len(lhs) == 2 { + if x.mode == valueok && l == 2 { // comma-ok expression x.mode = value check.assignVar(lhs[0], &x) @@ -240,7 +250,7 @@ func (check *checker) assignVars(lhs, rhs []ast.Expr) { } } - check.errorf(rhs[0].Pos(), "assignment count mismatch: %d = %d", len(lhs), len(rhs)) + check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r) } func (check *checker) shortVarDecl(lhs, rhs []ast.Expr) { diff --git a/go/types/resolver.go b/go/types/resolver.go index ec41215b4d..f7e6b0a519 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -42,11 +42,13 @@ type mdecl struct { meth *ast.FuncDecl } -// A projExpr projects the index'th value of a multi-valued expression. -// projExpr implements ast.Expr. -type projExpr struct { - lhs []*Var // all variables on the lhs - ast.Expr // rhs +// A multiExpr describes the lhs variables and a single but +// (expected to be) multi-valued rhs init expr of a variable +// declaration. +type multiExpr struct { + lhs []*Var + rhs []ast.Expr // len(rhs) == 1 + ast.Expr // dummy to satisfy ast.Expr interface } // arityMatch checks that the lhs and rhs of a const or var decl @@ -227,7 +229,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) { // (lhs may not be fully set up yet, but // that's fine because declare simply collects // the information for later processing.) - init = &projExpr{lhs, s.Values[0]} + init = &multiExpr{lhs, s.Values, nil} default: if i < len(s.Values) { init = s.Values[i] @@ -421,68 +423,23 @@ func (check *checker) declareVar(obj *Var, typ, init ast.Expr) { obj.typ = check.typ(typ, nil, false) } + // check initialization if init == nil { if typ == nil { + // error reported before by arityMatch obj.typ = Typ[Invalid] } - return // error reported before + return } - // unpack projection expression, if any - proj, multi := init.(*projExpr) - if multi { - init = proj.Expr + if m, _ := init.(*multiExpr); m != nil { + check.initVars(m.lhs, m.rhs, true) + return } var x operand check.expr(&x, init) - if x.mode == invalid { - goto Error - } - - if multi { - if t, ok := x.typ.(*Tuple); ok && len(proj.lhs) == t.Len() { - // function result - x.mode = value - for i, lhs := range proj.lhs { - x.expr = nil // TODO(gri) should do better here - x.typ = t.At(i).typ - check.initVar(lhs, &x) - } - return - } - - if x.mode == valueok && len(proj.lhs) == 2 { - // comma-ok expression - x.mode = value - check.initVar(proj.lhs[0], &x) - - x.typ = Typ[UntypedBool] - check.initVar(proj.lhs[1], &x) - return - } - - // TODO(gri) better error message - check.errorf(proj.lhs[0].Pos(), "assignment count mismatch") - goto Error - } - check.initVar(obj, &x) - return - -Error: - // mark all involved variables so we can avoid repeated error messages - if multi { - for _, obj := range proj.lhs { - if obj.typ == nil { - obj.typ = Typ[Invalid] - obj.visited = true - } - } - } else if obj.typ == nil { - obj.typ = Typ[Invalid] - } - return } func (check *checker) declareType(obj *TypeName, typ ast.Expr, def *Named, cycleOk bool) { @@ -641,7 +598,7 @@ func (check *checker) declStmt(decl ast.Decl) { } case token.VAR: - // For declareVar called with a projExpr we need the fully + // For declareVar called with a multiExpr we need the fully // initialized lhs. Compute it in a separate pre-pass. lhs := make([]*Var, len(s.Names)) for i, name := range s.Names { @@ -656,8 +613,8 @@ func (check *checker) declStmt(decl ast.Decl) { // lhs and rhs match init = s.Values[i] case 1: - // rhs must be a multi-valued expression - init = &projExpr{lhs, s.Values[0]} + // rhs is expected to be a multi-valued expression + init = &multiExpr{lhs, s.Values, nil} default: if i < len(s.Values) { init = s.Values[i] diff --git a/go/types/testdata/decls1.src b/go/types/testdata/decls1.src index d902b577ae..a881e773af 100644 --- a/go/types/testdata/decls1.src +++ b/go/types/testdata/decls1.src @@ -77,7 +77,7 @@ var ( u2 = iface.([]int) u3 = iface.(a /* ERROR "not a type" */ ) u4, ok = iface.(int) - u5 /* ERROR "assignment count mismatch" */ , ok2, ok3 = iface.(int) + u5, ok2, ok3 = iface /* ERROR "assignment count mismatch" */ .(int) ) // Constant expression initializations diff --git a/go/types/testdata/vardecl.src b/go/types/testdata/vardecl.src index 4f90b091e7..5914e72093 100644 --- a/go/types/testdata/vardecl.src +++ b/go/types/testdata/vardecl.src @@ -4,7 +4,10 @@ package vardecl +// Prerequisites. func f() {} +func g() (x, y int) { return } +var m map[string]int // Var decls must have a type or an initializer. var _ int @@ -21,23 +24,39 @@ var _ = f /* ERROR "used as value" */ () // Identifier and expression arity must match. var _, _ = 1, 2 var _ = 1, 2 /* ERROR "extra init expr 2" */ -var _ /* ERROR "assignment count mismatch" */ , _ = 1 // TODO(gri) better error message here +var _, _ = 1 /* ERROR "assignment count mismatch" */ var _, _, _ /* ERROR "missing init expr for _" */ = 1, 2 +var _ = g /* ERROR "2-valued expr" */ () +var _, _ = g() +var _, _, _ = g /* ERROR "assignment count mismatch" */ () + +var _ = m["foo"] +var _, _ = m["foo"] +var _, _, _ = m /* ERROR "assignment count mismatch" */ ["foo"] + var _, _ int = 1, 2 var _ int = 1, 2 /* ERROR "extra init expr 2" */ -var _ /* ERROR "assignment count mismatch" */ , _ int = 1 // TODO(gri) better error message here +var _, _ int = 1 /* ERROR "assignment count mismatch" */ var _, _, _ /* ERROR "missing init expr for _" */ int = 1, 2 var ( _, _ = 1, 2 _ = 1, 2 /* ERROR "extra init expr 2" */ - _ /* ERROR "assignment count mismatch" */ , _ = 1 // TODO(gri) better error message here + _, _ = 1 /* ERROR "assignment count mismatch" */ _, _, _ /* ERROR "missing init expr for _" */ = 1, 2 + _ = g /* ERROR "2-valued expr" */ () + _, _ = g() + _, _, _ = g /* ERROR "assignment count mismatch" */ () + + _ = m["foo"] + _, _ = m["foo"] + _, _, _ = m /* ERROR "assignment count mismatch" */ ["foo"] + _, _ int = 1, 2 _ int = 1, 2 /* ERROR "extra init expr 2" */ - _ /* ERROR "assignment count mismatch" */ , _ int = 1 // TODO(gri) better error message here + _, _ int = 1 /* ERROR "assignment count mismatch" */ _, _, _ /* ERROR "missing init expr for _" */ int = 1, 2 )