defer tracePrint("typecheckas", n)(nil)
}
- // delicate little dance.
- // the definition of n may refer to this assignment
- // as its definition, in which case it will call typecheckas.
- // in that case, do not call typecheck back, or it will cycle.
- // if the variable has a type (ntype) then typechecking
- // will not look at defn, so it is okay (and desirable,
- // so that the conversion below happens).
- n.X = Resolve(n.X)
-
- if !ir.DeclaredBy(n.X, n) || n.X.Name().Ntype != nil {
+ if n.Y == nil {
n.X = AssignExpr(n.X)
+ return
}
- // Use ctxMultiOK so we can emit an "N variables but M values" error
- // to be consistent with typecheckas2 (#26616).
- n.Y = typecheck(n.Y, ctxExpr|ctxMultiOK)
- checkassign(n, n.X)
- if n.Y != nil && n.Y.Type() != nil {
- if n.Y.Type().IsFuncArgStruct() {
- base.Errorf("assignment mismatch: 1 variable but %v returns %d values", n.Y.(*ir.CallExpr).X, n.Y.Type().NumFields())
- // Multi-value RHS isn't actually valid for OAS; nil out
- // to indicate failed typechecking.
- n.Y.SetType(nil)
- } else if n.X.Type() != nil {
- n.Y = AssignConv(n.Y, n.X.Type(), "assignment")
- }
- }
-
- if ir.DeclaredBy(n.X, n) && n.X.Name().Ntype == nil {
- n.Y = DefaultLit(n.Y, nil)
- n.X.SetType(n.Y.Type())
- }
-
- // second half of dance.
- // now that right is done, typecheck the left
- // just to get it over with. see dance above.
- n.SetTypecheck(1)
+ lhs, rhs := []ir.Node{n.X}, []ir.Node{n.Y}
+ assign(n, lhs, rhs)
+ n.X, n.Y = lhs[0], rhs[0]
- if n.X.Typecheck() == 0 {
- n.X = AssignExpr(n.X)
- }
+ // TODO(mdempsky): This seems out of place.
if !ir.IsBlank(n.X) {
types.CheckSize(n.X.Type()) // ensure width is calculated for backend
}
defer tracePrint("typecheckas2", n)(nil)
}
- ls := n.Lhs
- for i1, n1 := range ls {
- // delicate little dance.
- n1 = Resolve(n1)
- ls[i1] = n1
+ assign(n, n.Lhs, n.Rhs)
+}
+
+func assign(stmt ir.Node, lhs, rhs []ir.Node) {
+ // delicate little dance.
+ // the definition of lhs may refer to this assignment
+ // as its definition, in which case it will call typecheckas.
+ // in that case, do not call typecheck back, or it will cycle.
+ // if the variable has a type (ntype) then typechecking
+ // will not look at defn, so it is okay (and desirable,
+ // so that the conversion below happens).
- if !ir.DeclaredBy(n1, n) || n1.Name().Ntype != nil {
- ls[i1] = AssignExpr(ls[i1])
+ checkLHS := func(i int, typ *types.Type) {
+ lhs[i] = Resolve(lhs[i])
+ if n := lhs[i]; typ != nil && ir.DeclaredBy(n, stmt) && n.Name().Ntype == nil {
+ if typ.Kind() != types.TNIL {
+ n.SetType(defaultType(typ))
+ } else {
+ base.Errorf("use of untyped nil")
+ }
}
+ if lhs[i].Typecheck() == 0 {
+ lhs[i] = AssignExpr(lhs[i])
+ }
+ checkassign(stmt, lhs[i])
}
- cl := len(n.Lhs)
- cr := len(n.Rhs)
- if cl > 1 && cr == 1 {
- n.Rhs[0] = typecheck(n.Rhs[0], ctxExpr|ctxMultiOK)
- } else {
- Exprs(n.Rhs)
- }
- checkassignlist(n, n.Lhs)
-
- var l ir.Node
- var r ir.Node
- if cl == cr {
- // easy
- ls := n.Lhs
- rs := n.Rhs
- for il, nl := range ls {
- nr := rs[il]
- if nl.Type() != nil && nr.Type() != nil {
- rs[il] = AssignConv(nr, nl.Type(), "assignment")
- }
- if ir.DeclaredBy(nl, n) && nl.Name().Ntype == nil {
- rs[il] = DefaultLit(rs[il], nil)
- nl.SetType(rs[il].Type())
- }
+ assignType := func(i int, typ *types.Type) {
+ checkLHS(i, typ)
+ if typ != nil {
+ checkassignto(typ, lhs[i])
}
+ }
- goto out
+ cr := len(rhs)
+ if len(rhs) == 1 {
+ rhs[0] = typecheck(rhs[0], ctxExpr|ctxMultiOK)
+ if rtyp := rhs[0].Type(); rtyp != nil && rtyp.IsFuncArgStruct() {
+ cr = rtyp.NumFields()
+ }
+ } else {
+ Exprs(rhs)
}
- l = n.Lhs[0]
- r = n.Rhs[0]
+ // x, ok = y
+assignOK:
+ for len(lhs) == 2 && cr == 1 {
+ stmt := stmt.(*ir.AssignListStmt)
+ r := rhs[0]
- // x,y,z = f()
- if cr == 1 {
- if r.Type() == nil {
- goto out
- }
switch r.Op() {
- case ir.OCALLMETH, ir.OCALLINTER, ir.OCALLFUNC:
- if !r.Type().IsFuncArgStruct() {
- break
- }
- cr = r.Type().NumFields()
- if cr != cl {
- goto mismatch
- }
- r.(*ir.CallExpr).Use = ir.CallUseList
- n.SetOp(ir.OAS2FUNC)
- for i, l := range n.Lhs {
- f := r.Type().Field(i)
- if f.Type != nil && l.Type() != nil {
- checkassignto(f.Type, l)
- }
- if ir.DeclaredBy(l, n) && l.Name().Ntype == nil {
- l.SetType(f.Type)
- }
- }
- goto out
+ case ir.OINDEXMAP:
+ stmt.SetOp(ir.OAS2MAPR)
+ case ir.ORECV:
+ stmt.SetOp(ir.OAS2RECV)
+ case ir.ODOTTYPE:
+ r := r.(*ir.TypeAssertExpr)
+ stmt.SetOp(ir.OAS2DOTTYPE)
+ r.SetOp(ir.ODOTTYPE2)
+ default:
+ break assignOK
}
+
+ assignType(0, r.Type())
+ assignType(1, types.UntypedBool)
+ return
}
- // x, ok = y
- if cl == 2 && cr == 1 {
- if r.Type() == nil {
- goto out
- }
- switch r.Op() {
- case ir.OINDEXMAP, ir.ORECV, ir.ODOTTYPE:
- switch r.Op() {
- case ir.OINDEXMAP:
- n.SetOp(ir.OAS2MAPR)
- case ir.ORECV:
- n.SetOp(ir.OAS2RECV)
- case ir.ODOTTYPE:
- r := r.(*ir.TypeAssertExpr)
- n.SetOp(ir.OAS2DOTTYPE)
- r.SetOp(ir.ODOTTYPE2)
+ if len(lhs) != cr {
+ if r, ok := rhs[0].(*ir.CallExpr); ok && len(rhs) == 1 {
+ if r.Type() != nil {
+ base.ErrorfAt(stmt.Pos(), "assignment mismatch: %d variable%s but %v returns %d value%s", len(lhs), plural(len(lhs)), r.X, cr, plural(cr))
}
- if l.Type() != nil {
- checkassignto(r.Type(), l)
- }
- if ir.DeclaredBy(l, n) {
- l.SetType(r.Type())
- }
- l := n.Lhs[1]
- if l.Type() != nil && !l.Type().IsBoolean() {
- checkassignto(types.Types[types.TBOOL], l)
- }
- if ir.DeclaredBy(l, n) && l.Name().Ntype == nil {
- l.SetType(types.Types[types.TBOOL])
- }
- goto out
+ } else {
+ base.ErrorfAt(stmt.Pos(), "assignment mismatch: %d variable%s but %v value%s", len(lhs), plural(len(lhs)), len(rhs), plural(len(rhs)))
+ }
+
+ for i := range lhs {
+ checkLHS(i, nil)
}
+ return
}
-mismatch:
- switch r.Op() {
- default:
- base.Errorf("assignment mismatch: %d variables but %d values", cl, cr)
- case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
- r := r.(*ir.CallExpr)
- base.Errorf("assignment mismatch: %d variables but %v returns %d values", cl, r.X, cr)
+ // x,y,z = f()
+ if cr > len(rhs) {
+ stmt := stmt.(*ir.AssignListStmt)
+ stmt.SetOp(ir.OAS2FUNC)
+ r := rhs[0].(*ir.CallExpr)
+ r.Use = ir.CallUseList
+ rtyp := r.Type()
+
+ for i := range lhs {
+ assignType(i, rtyp.Field(i).Type)
+ }
+ return
}
- // second half of dance
-out:
- n.SetTypecheck(1)
- ls = n.Lhs
- for i1, n1 := range ls {
- if n1.Typecheck() == 0 {
- ls[i1] = AssignExpr(ls[i1])
+ for i, r := range rhs {
+ checkLHS(i, r.Type())
+ if lhs[i].Type() != nil {
+ rhs[i] = AssignConv(r, lhs[i].Type(), "assignment")
}
}
}
+func plural(n int) string {
+ if n == 1 {
+ return ""
+ }
+ return "s"
+}
+
// tcFor typechecks an OFOR node.
func tcFor(n *ir.ForStmt) ir.Node {
Stmts(n.Init())