]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.regabi] cmd/compile: cleanup for concrete types - typecheck
authorRuss Cox <rsc@golang.org>
Thu, 10 Dec 2020 23:42:42 +0000 (18:42 -0500)
committerRuss Cox <rsc@golang.org>
Thu, 17 Dec 2020 03:50:33 +0000 (03:50 +0000)
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 <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
src/cmd/compile/internal/gc/typecheck.go

index 6dc9c5820d130006c4a7efa58dbaa1048208fb3f..ef1955e88b80e1d736f101960662dae77db2e2fa 100644 (file)
@@ -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
        }