From: Russ Cox Date: Thu, 10 Dec 2020 23:42:42 +0000 (-0500) Subject: [dev.regabi] cmd/compile: cleanup for concrete types - typecheck X-Git-Tag: go1.17beta1~1539^2~311 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=4ac6a6317b;p=gostls13.git [dev.regabi] cmd/compile: cleanup for concrete types - typecheck An automated rewrite will add concrete type assertions after a test of n.Op(), when n can be safely type-asserted (meaning, n is not reassigned a different type, n is not reassigned and then used outside the scope of the type assertion, and so on). This sequence of CLs handles the code that the automated rewrite does not: adding specific types to function arguments, adjusting code not to call n.Left() etc when n may have multiple representations, and so on. This CL focuses on typecheck.go. Passes buildall w/ toolstash -cmp. Change-Id: I32d1d3b813b0a088b1750c9fd28cd858ed813f1d Reviewed-on: https://go-review.googlesource.com/c/go/+/277920 Trust: Russ Cox Reviewed-by: Matthew Dempsky --- diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 6dc9c5820d..ef1955e88b 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -250,7 +250,7 @@ func typecheck(n ir.Node, top int) (res ir.Node) { // Skip over parens. for n.Op() == ir.OPAREN { - n = n.Left() + n = n.(*ir.ParenExpr).Left() } // Resolve definition of name and value of iota lazily. @@ -439,10 +439,12 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { return n } - if n.Op() == ir.ONAME && n.SubOp() != 0 && top&ctxCallee == 0 { - base.Errorf("use of builtin %v not in function call", n.Sym()) - n.SetType(nil) - return n + if n.Op() == ir.ONAME { + if n.SubOp() != 0 && top&ctxCallee == 0 { + base.Errorf("use of builtin %v not in function call", n.Sym()) + n.SetType(nil) + return n + } } typecheckdef(n) @@ -651,19 +653,29 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { ir.OOROR, ir.OSUB, ir.OXOR: - var l ir.Node - var op ir.Op - var r ir.Node + var l, r ir.Node + var setLR func() + switch n := n.(type) { + case *ir.AssignOpStmt: + l, r = n.Left(), n.Right() + setLR = func() { n.SetLeft(l); n.SetRight(r) } + case *ir.BinaryExpr: + l, r = n.Left(), n.Right() + setLR = func() { n.SetLeft(l); n.SetRight(r) } + case *ir.LogicalExpr: + l, r = n.Left(), n.Right() + setLR = func() { n.SetLeft(l); n.SetRight(r) } + } + l = typecheck(l, ctxExpr) + r = typecheck(r, ctxExpr) + setLR() + if l.Type() == nil || r.Type() == nil { + n.SetType(nil) + return n + } + op := n.Op() if n.Op() == ir.OASOP { - n.SetLeft(typecheck(n.Left(), ctxExpr)) - n.SetRight(typecheck(n.Right(), ctxExpr)) - l = n.Left() - r = n.Right() - checkassign(n, n.Left()) - if l.Type() == nil || r.Type() == nil { - n.SetType(nil) - return n - } + checkassign(n, l) if n.Implicit() && !okforarith[l.Type().Kind()] { base.Errorf("invalid operation: %v (non-numeric type %v)", n, l.Type()) n.SetType(nil) @@ -671,20 +683,10 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } // TODO(marvin): Fix Node.EType type union. op = n.SubOp() - } else { - n.SetLeft(typecheck(n.Left(), ctxExpr)) - n.SetRight(typecheck(n.Right(), ctxExpr)) - l = n.Left() - r = n.Right() - if l.Type() == nil || r.Type() == nil { - n.SetType(nil) - return n - } - op = n.Op() } if op == ir.OLSH || op == ir.ORSH { r = defaultlit(r, types.Types[types.TUINT]) - n.SetRight(r) + setLR() t := r.Type() if !t.IsInteger() { base.Errorf("invalid operation: %v (shift count type %v, must be integer)", n, r.Type()) @@ -730,9 +732,8 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { // ideal mixed with non-ideal l, r = defaultlit2(l, r, false) + setLR() - n.SetLeft(l) - n.SetRight(r) if l.Type() == nil || r.Type() == nil { n.SetType(nil) return n @@ -768,7 +769,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { if r.Type().IsInterface() == l.Type().IsInterface() || l.Type().Width >= 1<<16 { l = ir.NewConvExpr(base.Pos, aop, r.Type(), l) l.SetTypecheck(1) - n.SetLeft(l) + setLR() } t = r.Type() @@ -789,7 +790,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { if r.Type().IsInterface() == l.Type().IsInterface() || r.Type().Width >= 1<<16 { r = ir.NewConvExpr(base.Pos, aop, l.Type(), r) r.SetTypecheck(1) - n.SetRight(r) + setLR() } t = l.Type() @@ -858,29 +859,30 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { if iscmp[n.Op()] { t = types.UntypedBool n.SetType(t) - n = evalConst(n) - if n.Op() != ir.OLITERAL { - l, r = defaultlit2(l, r, true) - n.SetLeft(l) - n.SetRight(r) + if con := evalConst(n); con.Op() == ir.OLITERAL { + return con } + l, r = defaultlit2(l, r, true) + setLR() + return n } if et == types.TSTRING && n.Op() == ir.OADD { // create or update OADDSTR node with list of strings in x + y + z + (w + v) + ... + var add *ir.AddStringExpr if l.Op() == ir.OADDSTR { - orig := n - n = l - n.SetPos(orig.Pos()) + add = l.(*ir.AddStringExpr) + add.SetPos(n.Pos()) } else { - n = ir.NodAt(n.Pos(), ir.OADDSTR, nil, nil) - n.PtrList().Set1(l) + add = ir.NewAddStringExpr(n.Pos(), []ir.Node{l}) } if r.Op() == ir.OADDSTR { - n.PtrList().AppendNodes(r.PtrList()) + add.PtrList().AppendNodes(r.PtrList()) } else { - n.PtrList().Append(r) + add.PtrList().Append(r) } + add.SetType(t) + return add } if (op == ir.ODIV || op == ir.OMOD) && ir.IsConst(r, constant.Int) { @@ -950,11 +952,12 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { return n case ir.OCOMPLIT: - return typecheckcomplit(n) + return typecheckcomplit(n.(*ir.CompLitExpr)) case ir.OXDOT, ir.ODOT: + n := n.(*ir.SelectorExpr) if n.Op() == ir.OXDOT { - n = adddot(n) + n = adddot(n).(*ir.SelectorExpr) n.SetOp(ir.ODOT) if n.Left() == nil { n.SetType(nil) @@ -1021,7 +1024,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } if (n.Op() == ir.ODOTINTER || n.Op() == ir.ODOTMETH) && top&ctxCallee == 0 { - n = typecheckpartialcall(n, s) + return typecheckpartialcall(n, s) } return n @@ -1286,9 +1289,9 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { return n } - n.SetLeft(nodAddr(n.Left())) - n.Left().SetImplicit(true) - n.SetLeft(typecheck(n.Left(), ctxExpr)) + addr := nodAddr(n.Left()) + addr.SetImplicit(true) + n.SetLeft(typecheck(addr, ctxExpr)) l = n.Left() } t := l.Type() @@ -1338,9 +1341,10 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { // call and call like case ir.OCALL: - n.(*ir.CallExpr).Use = ir.CallUseExpr + n := n.(*ir.CallExpr) + n.Use = ir.CallUseExpr if top == ctxStmt { - n.(*ir.CallExpr).Use = ir.CallUseStmt + n.Use = ir.CallUseStmt } typecheckslice(n.Init().Slice(), ctxStmt) // imported rewritten f(g()) calls (#30907) n.SetLeft(typecheck(n.Left(), ctxExpr|ctxType|ctxCallee)) @@ -1350,7 +1354,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { l := n.Left() - if l.Op() == ir.ONAME && l.SubOp() != 0 { + if l.Op() == ir.ONAME && l.(*ir.Name).SubOp() != 0 { if n.IsDDD() && l.SubOp() != ir.OAPPEND { base.Errorf("invalid use of ... with builtin %v", l) } @@ -1408,7 +1412,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { return n } - n = ir.NodAt(n.Pos(), ir.OCONV, arg, nil) + n := ir.NodAt(n.Pos(), ir.OCONV, arg, nil) n.SetType(l.Type()) return typecheck1(n, top) } @@ -1463,14 +1467,16 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { if t.NumResults() == 1 { n.SetType(l.Type().Results().Field(0).Type) - if n.Op() == ir.OCALLFUNC && n.Left().Op() == ir.ONAME && isRuntimePkg(n.Left().Sym().Pkg) && n.Left().Sym().Name == "getg" { - // Emit code for runtime.getg() directly instead of calling function. - // Most such rewrites (for example the similar one for math.Sqrt) should be done in walk, - // so that the ordering pass can make sure to preserve the semantics of the original code - // (in particular, the exact time of the function call) by introducing temporaries. - // In this case, we know getg() always returns the same result within a given function - // and we want to avoid the temporaries, so we do the rewrite earlier than is typical. - n.SetOp(ir.OGETG) + if n.Op() == ir.OCALLFUNC && n.Left().Op() == ir.ONAME { + if sym := n.Left().(*ir.Name).Sym(); isRuntimePkg(sym.Pkg) && sym.Name == "getg" { + // Emit code for runtime.getg() directly instead of calling function. + // Most such rewrites (for example the similar one for math.Sqrt) should be done in walk, + // so that the ordering pass can make sure to preserve the semantics of the original code + // (in particular, the exact time of the function call) by introducing temporaries. + // In this case, we know getg() always returns the same result within a given function + // and we want to avoid the temporaries, so we do the rewrite earlier than is typical. + n.SetOp(ir.OGETG) + } } return n } @@ -1733,6 +1739,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { return n case ir.OCONV: + n := n.(*ir.ConvExpr) checkwidth(n.Type()) // ensure width is calculated for backend n.SetLeft(typecheck(n.Left(), ctxExpr)) n.SetLeft(convlit1(n.Left(), n.Type(), true, nil)) @@ -1771,7 +1778,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { case ir.OSTR2RUNES: if n.Left().Op() == ir.OLITERAL { - n = stringtoruneslit(n) + return stringtoruneslit(n) } } return n @@ -1881,8 +1888,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } nn.SetType(t) - n = nn - return n + return nn case ir.ONEW: if n.Left() == nil { @@ -1990,6 +1996,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { // statements case ir.OAS: + n := n.(*ir.AssignStmt) typecheckas(n) // Code that creates temps does not bother to set defn, so do it here. @@ -1999,7 +2006,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { return n case ir.OAS2: - typecheckas2(n) + typecheckas2(n.(*ir.AssignListStmt)) return n case ir.OBREAK, @@ -2026,6 +2033,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { return n case ir.ODEFER, ir.OGO: + n := n.(*ir.GoDeferStmt) n.SetLeft(typecheck(n.Left(), ctxStmt|ctxExpr)) if !n.Left().Diag() { checkdefergo(n) @@ -2083,15 +2091,15 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { return n case ir.OSELECT: - typecheckselect(n) + typecheckselect(n.(*ir.SelectStmt)) return n case ir.OSWITCH: - typecheckswitch(n) + typecheckswitch(n.(*ir.SwitchStmt)) return n case ir.ORANGE: - typecheckrange(n) + typecheckrange(n.(*ir.RangeStmt)) return n case ir.OTYPESW: @@ -2119,13 +2127,26 @@ func typecheck1(n ir.Node, top int) (res ir.Node) { } func typecheckargs(n ir.Node) { - if n.List().Len() != 1 || n.IsDDD() { - typecheckslice(n.List().Slice(), ctxExpr) + var list []ir.Node + switch n := n.(type) { + default: + base.Fatalf("typecheckargs %+v", n.Op()) + case *ir.CallExpr: + list = n.List().Slice() + if n.IsDDD() { + typecheckslice(list, ctxExpr) + return + } + case *ir.ReturnStmt: + list = n.List().Slice() + } + if len(list) != 1 { + typecheckslice(list, ctxExpr) return } - typecheckslice(n.List().Slice(), ctxExpr|ctxMultiOK) - t := n.List().First().Type() + typecheckslice(list, ctxExpr|ctxMultiOK) + t := list[0].Type() if t == nil || !t.IsFuncArgStruct() { return } @@ -2138,7 +2159,7 @@ func typecheckargs(n ir.Node) { } as := ir.Nod(ir.OAS2, nil, nil) - as.PtrRlist().AppendNodes(n.PtrList()) + as.PtrRlist().Append(list...) // If we're outside of function context, then this call will // be executed during the generated init function. However, @@ -2149,16 +2170,24 @@ func typecheckargs(n ir.Node) { if static { Curfn = initTodo } + list = nil for _, f := range t.FieldSlice() { t := temp(f.Type) as.PtrInit().Append(ir.Nod(ir.ODCL, t, nil)) as.PtrList().Append(t) - n.PtrList().Append(t) + list = append(list, t) } if static { Curfn = nil } + switch n := n.(type) { + case *ir.CallExpr: + n.PtrList().Set(list) + case *ir.ReturnStmt: + n.PtrList().Set(list) + } + n.PtrInit().Append(typecheck(as, ctxStmt)) } @@ -2201,7 +2230,7 @@ func checksliceconst(lo ir.Node, hi ir.Node) bool { return true } -func checkdefergo(n ir.Node) { +func checkdefergo(n *ir.GoDeferStmt) { what := "defer" if n.Op() == ir.OGO { what = "go" @@ -2269,13 +2298,12 @@ func implicitstar(n ir.Node) ir.Node { if !t.IsArray() { return n } - n = ir.Nod(ir.ODEREF, n, nil) - n.SetImplicit(true) - n = typecheck(n, ctxExpr) - return n + star := ir.Nod(ir.ODEREF, n, nil) + star.SetImplicit(true) + return typecheck(star, ctxExpr) } -func needOneArg(n ir.Node, f string, args ...interface{}) (ir.Node, bool) { +func needOneArg(n *ir.CallExpr, f string, args ...interface{}) (ir.Node, bool) { if n.List().Len() == 0 { p := fmt.Sprintf(f, args...) base.Errorf("missing argument to %s: %v", p, n) @@ -2291,7 +2319,7 @@ func needOneArg(n ir.Node, f string, args ...interface{}) (ir.Node, bool) { return n.List().First(), true } -func needTwoArgs(n ir.Node) (ir.Node, ir.Node, bool) { +func needTwoArgs(n *ir.CallExpr) (ir.Node, ir.Node, bool) { if n.List().Len() != 2 { if n.List().Len() < 2 { base.Errorf("not enough arguments in call to %v", n) @@ -2334,7 +2362,7 @@ func lookdot1(errnode ir.Node, s *types.Sym, t *types.Type, fs *types.Fields, do // typecheckMethodExpr checks selector expressions (ODOT) where the // base expression is a type expression (OTYPE). -func typecheckMethodExpr(n ir.Node) (res ir.Node) { +func typecheckMethodExpr(n *ir.SelectorExpr) (res ir.Node) { if enableTrace && base.Flag.LowerT { defer tracePrint("typecheckMethodExpr", n)(&res) } @@ -2417,7 +2445,7 @@ func derefall(t *types.Type) *types.Type { return t } -func lookdot(n ir.Node, t *types.Type, dostrcmp int) *types.Field { +func lookdot(n *ir.SelectorExpr, t *types.Type, dostrcmp int) *types.Field { s := n.Sym() dowidth(t) @@ -2449,14 +2477,14 @@ func lookdot(n ir.Node, t *types.Type, dostrcmp int) *types.Field { n.SetType(f1.Type) if t.IsInterface() { if n.Left().Type().IsPtr() { - n.SetLeft(ir.Nod(ir.ODEREF, n.Left(), nil)) // implicitstar - n.Left().SetImplicit(true) - n.SetLeft(typecheck(n.Left(), ctxExpr)) + star := ir.Nod(ir.ODEREF, n.Left(), nil) + star.SetImplicit(true) + n.SetLeft(typecheck(star, ctxExpr)) } n.SetOp(ir.ODOTINTER) } - n.(*ir.SelectorExpr).Selection = f1 + n.Selection = f1 return f1 } @@ -2471,13 +2499,13 @@ func lookdot(n ir.Node, t *types.Type, dostrcmp int) *types.Field { if !types.Identical(rcvr, tt) { if rcvr.IsPtr() && types.Identical(rcvr.Elem(), tt) { checklvalue(n.Left(), "call pointer method on") - n.SetLeft(nodAddr(n.Left())) - n.Left().SetImplicit(true) - n.SetLeft(typecheck(n.Left(), ctxType|ctxExpr)) + addr := nodAddr(n.Left()) + addr.SetImplicit(true) + n.SetLeft(typecheck(addr, ctxType|ctxExpr)) } else if tt.IsPtr() && (!rcvr.IsPtr() || rcvr.IsPtr() && rcvr.Elem().NotInHeap()) && types.Identical(tt.Elem(), rcvr) { - n.SetLeft(ir.Nod(ir.ODEREF, n.Left(), nil)) - n.Left().SetImplicit(true) - n.SetLeft(typecheck(n.Left(), ctxType|ctxExpr)) + star := ir.Nod(ir.ODEREF, n.Left(), nil) + star.SetImplicit(true) + n.SetLeft(typecheck(star, ctxType|ctxExpr)) } else if tt.IsPtr() && tt.Elem().IsPtr() && types.Identical(derefall(tt), derefall(rcvr)) { base.Errorf("calling method %v with receiver %L requires explicit dereference", n.Sym(), n.Left()) for tt.IsPtr() { @@ -2485,9 +2513,9 @@ func lookdot(n ir.Node, t *types.Type, dostrcmp int) *types.Field { if rcvr.IsPtr() && !tt.Elem().IsPtr() { break } - n.SetLeft(ir.Nod(ir.ODEREF, n.Left(), nil)) - n.Left().SetImplicit(true) - n.SetLeft(typecheck(n.Left(), ctxType|ctxExpr)) + star := ir.Nod(ir.ODEREF, n.Left(), nil) + star.SetImplicit(true) + n.SetLeft(typecheck(star, ctxType|ctxExpr)) tt = tt.Elem() } } else { @@ -2495,13 +2523,16 @@ func lookdot(n ir.Node, t *types.Type, dostrcmp int) *types.Field { } } - pll := n - ll := n.Left() - for ll.Left() != nil && (ll.Op() == ir.ODOT || ll.Op() == ir.ODOTPTR || ll.Op() == ir.ODEREF) { - pll = ll - ll = ll.Left() + implicit, ll := n.Implicit(), n.Left() + for ll != nil && (ll.Op() == ir.ODOT || ll.Op() == ir.ODOTPTR || ll.Op() == ir.ODEREF) { + switch l := ll.(type) { + case *ir.SelectorExpr: + implicit, ll = l.Implicit(), l.Left() + case *ir.StarExpr: + implicit, ll = l.Implicit(), l.Left() + } } - if pll.Implicit() && ll.Type().IsPtr() && ll.Type().Sym() != nil && ll.Type().Sym().Def != nil && ir.AsNode(ll.Type().Sym().Def).Op() == ir.OTYPE { + if implicit && ll.Type().IsPtr() && ll.Type().Sym() != nil && ll.Type().Sym().Def != nil && ir.AsNode(ll.Type().Sym().Def).Op() == ir.OTYPE { // It is invalid to automatically dereference a named pointer type when selecting a method. // Make n.Left == ll to clarify error message. n.SetLeft(ll) @@ -2512,7 +2543,7 @@ func lookdot(n ir.Node, t *types.Type, dostrcmp int) *types.Field { n.SetOffset(f2.Offset) n.SetType(f2.Type) n.SetOp(ir.ODOTMETH) - n.(*ir.SelectorExpr).Selection = f2 + n.Selection = f2 return f2 } @@ -2742,8 +2773,12 @@ func iscomptype(t *types.Type) bool { // pushtype adds elided type information for composite literals if // appropriate, and returns the resulting expression. -func pushtype(n ir.Node, t *types.Type) ir.Node { - if n == nil || n.Op() != ir.OCOMPLIT || n.Right() != nil { +func pushtype(nn ir.Node, t *types.Type) ir.Node { + if nn == nil || nn.Op() != ir.OCOMPLIT { + return nn + } + n := nn.(*ir.CompLitExpr) + if n.Right() != nil { return n } @@ -2756,16 +2791,16 @@ func pushtype(n ir.Node, t *types.Type) ir.Node { // For *T, return &T{...}. n.SetRight(ir.TypeNode(t.Elem())) - n = nodAddrAt(n.Pos(), n) - n.SetImplicit(true) + addr := ir.NodAt(n.Pos(), ir.OADDR, n, nil) + addr.SetImplicit(true) + return addr } - return n } // The result of typecheckcomplit MUST be assigned back to n, e.g. // n.Left = typecheckcomplit(n.Left) -func typecheckcomplit(n ir.Node) (res ir.Node) { +func typecheckcomplit(n *ir.CompLitExpr) (res ir.Node) { if enableTrace && base.Flag.LowerT { defer tracePrint("typecheckcomplit", n)(&res) } @@ -2782,7 +2817,7 @@ func typecheckcomplit(n ir.Node) (res ir.Node) { } // Save original node (including n.Right) - n.(ir.OrigNode).SetOrig(ir.Copy(n)) + n.SetOrig(ir.Copy(n)) setlineno(n.Right()) @@ -2833,6 +2868,7 @@ func typecheckcomplit(n ir.Node) (res ir.Node) { base.Errorf("missing key in map literal") continue } + l := l.(*ir.KeyExpr) r := l.Left() r = pushtype(r, t.Key()) @@ -2876,9 +2912,9 @@ func typecheckcomplit(n ir.Node) (res ir.Node) { } // No pushtype allowed here. Must name fields for that. n1 = assignconv(n1, f.Type, "field value") - n1 = nodSym(ir.OSTRUCTKEY, n1, f.Sym) - n1.SetOffset(f.Offset) - ls[i] = n1 + sk := nodSym(ir.OSTRUCTKEY, n1, f.Sym) + sk.SetOffset(f.Offset) + ls[i] = sk } if len(ls) < t.NumFields() { base.Errorf("too few values in %v", n) @@ -2892,7 +2928,8 @@ func typecheckcomplit(n ir.Node) (res ir.Node) { setlineno(l) if l.Op() == ir.OKEY { - key := l.Left() + kv := l.(*ir.KeyExpr) + key := kv.Left() // Sym might have resolved to name in other top-level // package, because of import dot. Redirect to correct sym @@ -2911,7 +2948,7 @@ func typecheckcomplit(n ir.Node) (res ir.Node) { continue } - l = ir.NewStructKeyExpr(l.Pos(), s, l.Right()) + l = ir.NewStructKeyExpr(l.Pos(), s, kv.Right()) ls[i] = l } @@ -2923,6 +2960,7 @@ func typecheckcomplit(n ir.Node) (res ir.Node) { ls[i] = typecheck(ls[i], ctxExpr) continue } + l := l.(*ir.StructKeyExpr) f := lookdot1(nil, l.Sym(), t, t.Fields(), 0) if f == nil { @@ -2983,8 +3021,9 @@ func typecheckarraylit(elemType *types.Type, bound int64, elts []ir.Node, ctx st for i, elt := range elts { setlineno(elt) r := elts[i] - var kv ir.Node + var kv *ir.KeyExpr if elt.Op() == ir.OKEY { + elt := elt.(*ir.KeyExpr) elt.SetLeft(typecheck(elt.Left(), ctxExpr)) key = indexconst(elt.Left()) if key < 0 { @@ -3104,9 +3143,9 @@ func checkassign(stmt ir.Node, n ir.Node) { } switch { - case n.Op() == ir.ODOT && n.Left().Op() == ir.OINDEXMAP: + case n.Op() == ir.ODOT && n.(*ir.SelectorExpr).Left().Op() == ir.OINDEXMAP: base.Errorf("cannot assign to struct field %v in map", n) - case (n.Op() == ir.OINDEX && n.Left().Type().IsString()) || n.Op() == ir.OSLICESTR: + case (n.Op() == ir.OINDEX && n.(*ir.IndexExpr).Left().Type().IsString()) || n.Op() == ir.OSLICESTR: base.Errorf("cannot assign to %v (strings are immutable)", n) case n.Op() == ir.OLITERAL && n.Sym() != nil && isGoConst(n): base.Errorf("cannot assign to %v (declared const)", n) @@ -3147,19 +3186,40 @@ func samesafeexpr(l ir.Node, r ir.Node) bool { return l == r case ir.ODOT, ir.ODOTPTR: + l := l.(*ir.SelectorExpr) + r := r.(*ir.SelectorExpr) return l.Sym() != nil && r.Sym() != nil && l.Sym() == r.Sym() && samesafeexpr(l.Left(), r.Left()) - case ir.ODEREF, ir.OCONVNOP, - ir.ONOT, ir.OBITNOT, ir.OPLUS, ir.ONEG: + case ir.ODEREF: + l := l.(*ir.StarExpr) + r := r.(*ir.StarExpr) + return samesafeexpr(l.Left(), r.Left()) + + case ir.ONOT, ir.OBITNOT, ir.OPLUS, ir.ONEG: + l := l.(*ir.UnaryExpr) + r := r.(*ir.UnaryExpr) + return samesafeexpr(l.Left(), r.Left()) + + case ir.OCONVNOP: + l := l.(*ir.ConvExpr) + r := r.(*ir.ConvExpr) return samesafeexpr(l.Left(), r.Left()) case ir.OCONV: + l := l.(*ir.ConvExpr) + r := r.(*ir.ConvExpr) // Some conversions can't be reused, such as []byte(str). // Allow only numeric-ish types. This is a bit conservative. return issimple[l.Type().Kind()] && samesafeexpr(l.Left(), r.Left()) - case ir.OINDEX, ir.OINDEXMAP, - ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OMOD: + case ir.OINDEX, ir.OINDEXMAP: + l := l.(*ir.IndexExpr) + r := r.(*ir.IndexExpr) + return samesafeexpr(l.Left(), r.Left()) && samesafeexpr(l.Right(), r.Right()) + + case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OMOD: + l := l.(*ir.BinaryExpr) + r := r.(*ir.BinaryExpr) return samesafeexpr(l.Left(), r.Left()) && samesafeexpr(l.Right(), r.Right()) case ir.OLITERAL: @@ -3175,7 +3235,7 @@ func samesafeexpr(l ir.Node, r ir.Node) bool { // type check assignment. // if this assignment is the definition of a var on the left side, // fill in the var's type. -func typecheckas(n ir.Node) { +func typecheckas(n *ir.AssignStmt) { if enableTrace && base.Flag.LowerT { defer tracePrint("typecheckas", n)(nil) } @@ -3199,7 +3259,7 @@ func typecheckas(n ir.Node) { checkassign(n, n.Left()) if n.Right() != nil && n.Right().Type() != nil { if n.Right().Type().IsFuncArgStruct() { - base.Errorf("assignment mismatch: 1 variable but %v returns %d values", n.Right().Left(), n.Right().Type().NumFields()) + base.Errorf("assignment mismatch: 1 variable but %v returns %d values", n.Right().(*ir.CallExpr).Left(), n.Right().Type().NumFields()) // Multi-value RHS isn't actually valid for OAS; nil out // to indicate failed typechecking. n.Right().SetType(nil) @@ -3233,7 +3293,7 @@ func checkassignto(src *types.Type, dst ir.Node) { } } -func typecheckas2(n ir.Node) { +func typecheckas2(n *ir.AssignListStmt) { if enableTrace && base.Flag.LowerT { defer tracePrint("typecheckas2", n)(nil) } @@ -3400,7 +3460,7 @@ func typecheckfunc(n *ir.Func) { // The result of stringtoruneslit MUST be assigned back to n, e.g. // n.Left = stringtoruneslit(n.Left) -func stringtoruneslit(n ir.Node) ir.Node { +func stringtoruneslit(n *ir.ConvExpr) ir.Node { if n.Left().Op() != ir.OLITERAL || n.Left().Val().Kind() != constant.String { base.Fatalf("stringtoarraylit %v", n) } @@ -3683,19 +3743,25 @@ func markBreak(fn *ir.Func) { case ir.OBREAK: if n.Sym() == nil { - if implicit != nil { - implicit.SetHasBreak(true) - } + setHasBreak(implicit) } else { - if lab := labels[n.Sym()]; lab != nil { - lab.SetHasBreak(true) - } + setHasBreak(labels[n.Sym()]) } - case ir.OFOR, ir.OFORUNTIL, ir.OSWITCH, ir.OTYPESW, ir.OSELECT, ir.ORANGE: + case ir.OFOR, ir.OFORUNTIL, ir.OSWITCH, ir.OSELECT, ir.ORANGE: old := implicit implicit = n - sym := n.Sym() + var sym *types.Sym + switch n := n.(type) { + case *ir.ForStmt: + sym = n.Sym() + case *ir.RangeStmt: + sym = n.Sym() + case *ir.SelectStmt: + sym = n.Sym() + case *ir.SwitchStmt: + sym = n.Sym() + } if sym != nil { if labels == nil { // Map creation delayed until we need it - most functions don't. @@ -3715,6 +3781,39 @@ func markBreak(fn *ir.Func) { mark(fn) } +func controlLabel(n ir.Node) *types.Sym { + switch n := n.(type) { + default: + base.Fatalf("controlLabel %+v", n.Op()) + return nil + case *ir.ForStmt: + return n.Sym() + case *ir.RangeStmt: + return n.Sym() + case *ir.SelectStmt: + return n.Sym() + case *ir.SwitchStmt: + return n.Sym() + } +} + +func setHasBreak(n ir.Node) { + switch n := n.(type) { + default: + base.Fatalf("setHasBreak %+v", n.Op()) + case nil: + // ignore + case *ir.ForStmt: + n.SetHasBreak(true) + case *ir.RangeStmt: + n.SetHasBreak(true) + case *ir.SelectStmt: + n.SetHasBreak(true) + case *ir.SwitchStmt: + n.SetHasBreak(true) + } +} + // isTermNodes reports whether the Nodes list ends with a terminating statement. func isTermNodes(l ir.Nodes) bool { s := l.Slice() @@ -3752,23 +3851,32 @@ func isTermNode(n ir.Node) bool { case ir.OIF: return isTermNodes(n.Body()) && isTermNodes(n.Rlist()) - case ir.OSWITCH, ir.OTYPESW, ir.OSELECT: + case ir.OSWITCH: if n.HasBreak() { return false } def := false - for _, n1 := range n.List().Slice() { - if !isTermNodes(n1.Body()) { + for _, cas := range n.List().Slice() { + cas := cas.(*ir.CaseStmt) + if !isTermNodes(cas.Body()) { return false } - if n1.List().Len() == 0 { // default + if cas.List().Len() == 0 { // default def = true } } + return def - if n.Op() != ir.OSELECT && !def { + case ir.OSELECT: + if n.HasBreak() { return false } + for _, cas := range n.List().Slice() { + cas := cas.(*ir.CaseStmt) + if !isTermNodes(cas.Body()) { + return false + } + } return true }