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 {
// 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) {

View File

@ -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]

View File

@ -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

View File

@ -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
)