go.tools/go/types: reuse code for m:n assignments

- more test cases

R=adonovan
CC=golang-dev
https://golang.org/cl/10952044
This commit is contained in:
Robert Griesemer 2013-07-11 10:54:52 -07:00
parent 59f09dcb8c
commit 1f28df4b6c
4 changed files with 80 additions and 94 deletions

View File

@ -24,7 +24,7 @@ func (check *checker) assignment(x *operand, to Type) bool {
} }
if t, ok := x.typ.(*Tuple); ok { 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) check.errorf(x.pos(), "%d-valued expression %s used as single value", t.Len(), x)
x.mode = invalid x.mode = invalid
return false 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) { 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, // If the lhs and rhs have corresponding expressions,
// treat each matching pair as an individual pair. // treat each matching pair as an individual pair.
if len(lhs) == len(rhs) { if l == r {
var x operand var x operand
for i, e := range rhs { for i, e := range rhs {
check.expr(&x, e) 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 // Otherwise, the rhs must be a single expression (possibly
// a function call returning multiple values, or a comma-ok // a function call returning multiple values, or a comma-ok
// expression). // expression).
if len(rhs) == 1 { if r == 1 {
// len(lhs) > 1 // l > 1
// Start with rhs so we have expression types // Start with rhs so we have expression types
// for declarations with implicit types. // for declarations with implicit types.
var x operand var x operand
@ -163,8 +165,10 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) {
return return
} }
if t, ok := x.typ.(*Tuple); ok && len(lhs) == t.Len() { if t, ok := x.typ.(*Tuple); ok {
// function result // function result
r = t.Len()
if l == r {
for i, lhs := range lhs { for i, lhs := range lhs {
x.mode = value x.mode = value
x.expr = rhs[0] x.expr = rhs[0]
@ -173,8 +177,9 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, allowCommaOk bool) {
} }
return return
} }
}
if allowCommaOk && x.mode == valueok && len(lhs) == 2 { if allowCommaOk && x.mode == valueok && l == 2 {
// comma-ok expression // comma-ok expression
x.mode = value x.mode = value
check.initVar(lhs[0], &x) 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); // lhs variables may be function result parameters (return statement);
// use rhs position information for properly located error messages // use rhs position for properly located error messages
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)
invalidateVars(lhs) invalidateVars(lhs)
} }
func (check *checker) assignVars(lhs, rhs []ast.Expr) { 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, // If the lhs and rhs have corresponding expressions,
// treat each matching pair as an individual pair. // treat each matching pair as an individual pair.
if len(lhs) == len(rhs) { if l == r {
var x operand var x operand
for i, e := range rhs { for i, e := range rhs {
check.expr(&x, e) check.expr(&x, e)
@ -209,16 +216,18 @@ func (check *checker) assignVars(lhs, rhs []ast.Expr) {
// Otherwise, the rhs must be a single expression (possibly // Otherwise, the rhs must be a single expression (possibly
// a function call returning multiple values, or a comma-ok // a function call returning multiple values, or a comma-ok
// expression). // expression).
if len(rhs) == 1 { if r == 1 {
// len(lhs) > 1 // l > 1
var x operand var x operand
check.expr(&x, rhs[0]) check.expr(&x, rhs[0])
if x.mode == invalid { if x.mode == invalid {
return return
} }
if t, ok := x.typ.(*Tuple); ok && len(lhs) == t.Len() { if t, ok := x.typ.(*Tuple); ok {
// function result // function result
r = t.Len()
if l == r {
for i, lhs := range lhs { for i, lhs := range lhs {
x.mode = value x.mode = value
x.expr = rhs[0] x.expr = rhs[0]
@ -227,8 +236,9 @@ func (check *checker) assignVars(lhs, rhs []ast.Expr) {
} }
return return
} }
}
if x.mode == valueok && len(lhs) == 2 { if x.mode == valueok && l == 2 {
// comma-ok expression // comma-ok expression
x.mode = value x.mode = value
check.assignVar(lhs[0], &x) 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) { func (check *checker) shortVarDecl(lhs, rhs []ast.Expr) {

View File

@ -42,11 +42,13 @@ type mdecl struct {
meth *ast.FuncDecl meth *ast.FuncDecl
} }
// A projExpr projects the index'th value of a multi-valued expression. // A multiExpr describes the lhs variables and a single but
// projExpr implements ast.Expr. // (expected to be) multi-valued rhs init expr of a variable
type projExpr struct { // declaration.
lhs []*Var // all variables on the lhs type multiExpr struct {
ast.Expr // rhs 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 // 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 // (lhs may not be fully set up yet, but
// that's fine because declare simply collects // that's fine because declare simply collects
// the information for later processing.) // the information for later processing.)
init = &projExpr{lhs, s.Values[0]} init = &multiExpr{lhs, s.Values, nil}
default: default:
if i < len(s.Values) { if i < len(s.Values) {
init = s.Values[i] 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) obj.typ = check.typ(typ, nil, false)
} }
// check initialization
if init == nil { if init == nil {
if typ == nil { if typ == nil {
// error reported before by arityMatch
obj.typ = Typ[Invalid] obj.typ = Typ[Invalid]
} }
return // error reported before return
} }
// unpack projection expression, if any if m, _ := init.(*multiExpr); m != nil {
proj, multi := init.(*projExpr) check.initVars(m.lhs, m.rhs, true)
if multi { return
init = proj.Expr
} }
var x operand var x operand
check.expr(&x, init) 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) 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) { 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: 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. // initialized lhs. Compute it in a separate pre-pass.
lhs := make([]*Var, len(s.Names)) lhs := make([]*Var, len(s.Names))
for i, name := range s.Names { for i, name := range s.Names {
@ -656,8 +613,8 @@ func (check *checker) declStmt(decl ast.Decl) {
// lhs and rhs match // lhs and rhs match
init = s.Values[i] init = s.Values[i]
case 1: case 1:
// rhs must be a multi-valued expression // rhs is expected to be a multi-valued expression
init = &projExpr{lhs, s.Values[0]} init = &multiExpr{lhs, s.Values, nil}
default: default:
if i < len(s.Values) { if i < len(s.Values) {
init = s.Values[i] init = s.Values[i]

View File

@ -77,7 +77,7 @@ var (
u2 = iface.([]int) u2 = iface.([]int)
u3 = iface.(a /* ERROR "not a type" */ ) u3 = iface.(a /* ERROR "not a type" */ )
u4, ok = iface.(int) 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 // Constant expression initializations

View File

@ -4,7 +4,10 @@
package vardecl package vardecl
// Prerequisites.
func f() {} func f() {}
func g() (x, y int) { return }
var m map[string]int
// Var decls must have a type or an initializer. // Var decls must have a type or an initializer.
var _ int var _ int
@ -21,23 +24,39 @@ var _ = f /* ERROR "used as value" */ ()
// Identifier and expression arity must match. // Identifier and expression arity must match.
var _, _ = 1, 2 var _, _ = 1, 2
var _ = 1, 2 /* ERROR "extra init expr 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 _, _, _ /* 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
var _ int = 1, 2 /* ERROR "extra init expr 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 _, _, _ /* ERROR "missing init expr for _" */ int = 1, 2
var ( var (
_, _ = 1, 2 _, _ = 1, 2
_ = 1, 2 /* ERROR "extra init expr 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 _, _, _ /* 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
_ int = 1, 2 /* ERROR "extra init expr 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 _, _, _ /* ERROR "missing init expr for _" */ int = 1, 2
) )