]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: create/use noder2 transform functions for more node types
authorDan Scales <danscales@google.com>
Tue, 23 Mar 2021 17:19:11 +0000 (10:19 -0700)
committerDan Scales <danscales@google.com>
Thu, 25 Mar 2021 00:06:23 +0000 (00:06 +0000)
Pull out the transformation part of the typechecking functions for:
 - assignment statements
 - return statements
 - send statements
 - select statements
 - type conversions
 - normal function/method calls
 - index operations

The transform functions are like the original typechecking functions,
but with all code removed related to:
  - Detecting compile-time errors (already done by types2)
  - Setting the actual type of existing nodes (already done based on
    info from types2)
  - Dealing with untyped constants

Moved all the transformation functions to a separate file, transform.go.

Continuing with the same pattern, we delay transforming a node if it has
any type params in its args, marking it with a typecheck flag of 3, and
do the actual transformation during stenciling.

Assignment statements are tricky, since their transformation must be
delayed if any of the left or right-hands-sides are delayed.

Still to do are:
 - selector expressions (OXDOT)
 - composite literal expressions (OCOMPLIT)
 - builtin function calls

Change-Id: Ie608cadbbc69b40db0067a5536cf707dd974aacc
Reviewed-on: https://go-review.googlesource.com/c/go/+/304049
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/cmd/compile/internal/noder/helpers.go
src/cmd/compile/internal/noder/stencil.go
src/cmd/compile/internal/noder/stmt.go
src/cmd/compile/internal/noder/transform.go [new file with mode: 0644]
src/cmd/compile/internal/noder/validate.go
src/cmd/compile/internal/typecheck/expr.go
src/cmd/compile/internal/typecheck/subr.go

index e4a1a54fe86243b7ce5021eae111372cf74526a5..82428daa4ae314a1b7da8f4f42c002326e4fa5bb 100644 (file)
@@ -67,31 +67,6 @@ func Assert(pos src.XPos, x ir.Node, typ *types.Type) ir.Node {
        return typed(typ, ir.NewTypeAssertExpr(pos, x, nil))
 }
 
-// transformAdd transforms an addition operation (currently just addition of
-// strings). Equivalent to the "binary operators" case in typecheck.typecheck1.
-func transformAdd(n *ir.BinaryExpr) ir.Node {
-       l := n.X
-       if l.Type().IsString() {
-               var add *ir.AddStringExpr
-               if l.Op() == ir.OADDSTR {
-                       add = l.(*ir.AddStringExpr)
-                       add.SetPos(n.Pos())
-               } else {
-                       add = ir.NewAddStringExpr(n.Pos(), []ir.Node{l})
-               }
-               r := n.Y
-               if r.Op() == ir.OADDSTR {
-                       r := r.(*ir.AddStringExpr)
-                       add.List.Append(r.List.Take()...)
-               } else {
-                       add.List.Append(r)
-               }
-               add.SetType(l.Type())
-               return add
-       }
-       return n
-}
-
 func Binary(pos src.XPos, op ir.Op, typ *types.Type, x, y ir.Node) ir.Node {
        switch op {
        case ir.OANDAND, ir.OOROR:
@@ -124,7 +99,9 @@ func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool)
                        // the type.
                        return typed(typ, n)
                }
-               return typecheck.Expr(n)
+               n1 := transformConvCall(n)
+               n1.SetTypecheck(1)
+               return n1
        }
 
        if fun, ok := fun.(*ir.Name); ok && fun.BuiltinOp != 0 {
@@ -181,6 +158,11 @@ func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool)
                }
        }
 
+       n.Use = ir.CallUseExpr
+       if fun.Type().NumResults() == 0 {
+               n.Use = ir.CallUseStmt
+       }
+
        if fun.Op() == ir.OXDOT {
                if !fun.(*ir.SelectorExpr).X.Type().HasTParam() {
                        base.FatalfAt(pos, "Expecting type param receiver in %v", fun)
@@ -192,63 +174,18 @@ func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool)
                return n
        }
        if fun.Op() != ir.OFUNCINST {
-               // If no type params, do normal typechecking, since we're
-               // still missing some things done by tcCall (mainly
-               // typecheckaste/assignconvfn - implementing assignability of args
-               // to params).  This will convert OCALL to OCALLFUNC.
-               typecheck.Call(n)
+               // If no type params, do the normal call transformations. This
+               // will convert OCALL to OCALLFUNC.
+               transformCall(n)
+               typed(typ, n)
                return n
        }
 
        // Leave the op as OCALL, which indicates the call still needs typechecking.
-       n.Use = ir.CallUseExpr
-       if fun.Type().NumResults() == 0 {
-               n.Use = ir.CallUseStmt
-       }
        typed(typ, n)
        return n
 }
 
-// transformCompare transforms a compare operation (currently just equals/not
-// equals). Equivalent to the "comparison operators" case in
-// typecheck.typecheck1, including tcArith.
-func transformCompare(n *ir.BinaryExpr) {
-       if (n.Op() == ir.OEQ || n.Op() == ir.ONE) && !types.Identical(n.X.Type(), n.Y.Type()) {
-               // Comparison is okay as long as one side is assignable to the
-               // other. The only allowed case where the conversion is not CONVNOP is
-               // "concrete == interface". In that case, check comparability of
-               // the concrete type. The conversion allocates, so only do it if
-               // the concrete type is huge.
-               l, r := n.X, n.Y
-               lt, rt := l.Type(), r.Type()
-               converted := false
-               if rt.Kind() != types.TBLANK {
-                       aop, _ := typecheck.Assignop(lt, rt)
-                       if aop != ir.OXXX {
-                               types.CalcSize(lt)
-                               if rt.IsInterface() == lt.IsInterface() || lt.Width >= 1<<16 {
-                                       l = ir.NewConvExpr(base.Pos, aop, rt, l)
-                                       l.SetTypecheck(1)
-                               }
-
-                               converted = true
-                       }
-               }
-
-               if !converted && lt.Kind() != types.TBLANK {
-                       aop, _ := typecheck.Assignop(rt, lt)
-                       if aop != ir.OXXX {
-                               types.CalcSize(rt)
-                               if rt.IsInterface() == lt.IsInterface() || rt.Width >= 1<<16 {
-                                       r = ir.NewConvExpr(base.Pos, aop, lt, r)
-                                       r.SetTypecheck(1)
-                               }
-                       }
-               }
-               n.X, n.Y = l, r
-       }
-}
-
 func Compare(pos src.XPos, typ *types.Type, op ir.Op, x, y ir.Node) ir.Node {
        n := ir.NewBinaryExpr(pos, op, x, y)
        if x.Type().HasTParam() || y.Type().HasTParam() {
@@ -330,38 +267,16 @@ func method(typ *types.Type, index int) *types.Field {
 
 func Index(pos src.XPos, typ *types.Type, x, index ir.Node) ir.Node {
        n := ir.NewIndexExpr(pos, x, index)
-       // TODO(danscales): Temporary fix. Need to separate out the
-       // transformations done by the old typechecker (in tcIndex()), to be
-       // called here or after stenciling.
-       if x.Type().HasTParam() && x.Type().Kind() != types.TMAP &&
-               x.Type().Kind() != types.TSLICE && x.Type().Kind() != types.TARRAY {
-               // Old typechecker will complain if arg is not obviously a slice/array/map.
-               typed(typ, n)
+       if x.Type().HasTParam() {
+               // transformIndex needs to know exact type
+               n.SetType(typ)
+               n.SetTypecheck(3)
                return n
        }
-       return typecheck.Expr(n)
-}
-
-// transformSlice transforms a slice operation.  Equivalent to typecheck.tcSlice.
-func transformSlice(n *ir.SliceExpr) {
-       l := n.X
-       if l.Type().IsArray() {
-               addr := typecheck.NodAddr(n.X)
-               addr.SetImplicit(true)
-               typed(types.NewPtr(n.X.Type()), addr)
-               n.X = addr
-               l = addr
-       }
-       t := l.Type()
-       if t.IsString() {
-               n.SetOp(ir.OSLICESTR)
-       } else if t.IsPtr() && t.Elem().IsArray() {
-               if n.Op().IsSlice3() {
-                       n.SetOp(ir.OSLICE3ARR)
-               } else {
-                       n.SetOp(ir.OSLICEARR)
-               }
-       }
+       typed(typ, n)
+       // transformIndex will modify n.Type() for OINDEXMAP.
+       transformIndex(n)
+       return n
 }
 
 func Slice(pos src.XPos, typ *types.Type, x, low, high, max ir.Node) ir.Node {
@@ -399,7 +314,7 @@ func Unary(pos src.XPos, op ir.Op, x ir.Node) ir.Node {
 
 var one = constant.MakeInt64(1)
 
-func IncDec(pos src.XPos, op ir.Op, x ir.Node) ir.Node {
-       x = typecheck.AssignExpr(x)
+func IncDec(pos src.XPos, op ir.Op, x ir.Node) *ir.AssignOpStmt {
+       assert(x.Type() != nil)
        return ir.NewAssignOpStmt(pos, op, x, typecheck.DefaultLit(ir.NewBasicLit(pos, one), x.Type()))
 }
index 1b76bb27c55796e8ce43247bcea0cd342063b0fb..ba01f0424b97e4d65edb06c683281922ba484512 100644 (file)
@@ -95,10 +95,9 @@ func (g *irgen) stencil() {
                                copy(withRecv[1:], call.Args)
                                call.Args = withRecv
                        }
-                       // Do the typechecking of the Call now, which changes OCALL
+                       // Transform the Call now, which changes OCALL
                        // to OCALLFUNC and does typecheckaste/assignconvfn.
-                       call.SetTypecheck(0)
-                       typecheck.Call(call)
+                       transformCall(call)
                        modified = true
                })
 
@@ -372,16 +371,36 @@ func (subst *subster) node(n ir.Node) ir.Node {
                        // their instantiated type was known.
                        if typecheck.IsCmp(x.Op()) {
                                transformCompare(m.(*ir.BinaryExpr))
-                               m.SetTypecheck(1)
-                       } else if x.Op() == ir.OSLICE || x.Op() == ir.OSLICE3 {
-                               transformSlice(m.(*ir.SliceExpr))
-                               m.SetTypecheck(1)
-                       } else if x.Op() == ir.OADD {
-                               m = transformAdd(m.(*ir.BinaryExpr))
-                               m.SetTypecheck(1)
                        } else {
-                               base.Fatalf("Unexpected node with Typecheck() == 3")
+                               switch x.Op() {
+                               case ir.OSLICE:
+                               case ir.OSLICE3:
+                                       transformSlice(m.(*ir.SliceExpr))
+
+                               case ir.OADD:
+                                       m = transformAdd(m.(*ir.BinaryExpr))
+
+                               case ir.OINDEX:
+                                       transformIndex(m.(*ir.IndexExpr))
+
+                               case ir.OAS2:
+                                       as2 := m.(*ir.AssignListStmt)
+                                       transformAssign(as2, as2.Lhs, as2.Rhs)
+
+                               case ir.OAS:
+                                       as := m.(*ir.AssignStmt)
+                                       lhs, rhs := []ir.Node{as.X}, []ir.Node{as.Y}
+                                       transformAssign(as, lhs, rhs)
+
+                               case ir.OASOP:
+                                       as := m.(*ir.AssignOpStmt)
+                                       transformCheckAssign(as, as.X)
+
+                               default:
+                                       base.Fatalf("Unexpected node with Typecheck() == 3")
+                               }
                        }
+                       m.SetTypecheck(1)
                }
 
                switch x.Op() {
@@ -415,26 +434,25 @@ func (subst *subster) node(n ir.Node) ir.Node {
                case ir.OCALL:
                        call := m.(*ir.CallExpr)
                        if call.X.Op() == ir.OTYPE {
-                               // Do typechecking on a conversion, now that we
-                               // know the type argument.
-                               m.SetTypecheck(0)
-                               m = typecheck.Expr(m)
+                               // Transform the conversion, now that we know the
+                               // type argument.
+                               m = transformConvCall(m.(*ir.CallExpr))
+                               m.SetTypecheck(1)
                        } else if call.X.Op() == ir.OCALLPART {
-                               // Redo the typechecking, now that we know the method
-                               // value is being called.
+                               // Redo the typechecking of OXDOT, now that we
+                               // know the method value is being called. Then
+                               // transform the call.
                                call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
                                call.X.SetTypecheck(0)
                                call.X.SetType(nil)
                                typecheck.Callee(call.X)
-                               call.SetTypecheck(0)
-                               typecheck.Call(call)
+                               transformCall(call)
                        } else if call.X.Op() == ir.ODOT || call.X.Op() == ir.ODOTPTR {
                                // An OXDOT for a generic receiver was resolved to
                                // an access to a field which has a function
-                               // value. Typecheck the call to that function, now
+                               // value. Transform the call to that function, now
                                // that the OXDOT was resolved.
-                               call.SetTypecheck(0)
-                               typecheck.Call(call)
+                               transformCall(call)
                        } else if name := call.X.Name(); name != nil {
                                switch name.BuiltinOp {
                                case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OLEN, ir.OCAP, ir.OAPPEND:
index 31c6bfe5c8a92c9df326839bd289cb06320690ce..f85496be40bd05286607d414e1a166f7f88e6166 100644 (file)
@@ -27,15 +27,6 @@ func (g *irgen) stmts(stmts []syntax.Stmt) []ir.Node {
 }
 
 func (g *irgen) stmt(stmt syntax.Stmt) ir.Node {
-       // TODO(mdempsky): Remove dependency on typecheck.
-       n := g.stmt0(stmt)
-       if n != nil {
-               n.SetTypecheck(1)
-       }
-       return n
-}
-
-func (g *irgen) stmt0(stmt syntax.Stmt) ir.Node {
        switch stmt := stmt.(type) {
        case nil, *syntax.EmptyStmt:
                return nil
@@ -51,35 +42,75 @@ func (g *irgen) stmt0(stmt syntax.Stmt) ir.Node {
                return x
        case *syntax.SendStmt:
                n := ir.NewSendStmt(g.pos(stmt), g.expr(stmt.Chan), g.expr(stmt.Value))
-               // Need to do the AssignConv() in tcSend().
-               return typecheck.Stmt(n)
+               transformSend(n)
+               n.SetTypecheck(1)
+               return n
        case *syntax.DeclStmt:
                return ir.NewBlockStmt(g.pos(stmt), g.decls(stmt.DeclList))
 
        case *syntax.AssignStmt:
                if stmt.Op != 0 && stmt.Op != syntax.Def {
                        op := g.op(stmt.Op, binOps[:])
-                       // May need to insert ConvExpr nodes on the args in tcArith
+                       var n *ir.AssignOpStmt
                        if stmt.Rhs == nil {
-                               return typecheck.Stmt(IncDec(g.pos(stmt), op, g.expr(stmt.Lhs)))
+                               n = IncDec(g.pos(stmt), op, g.expr(stmt.Lhs))
+                       } else {
+                               n = ir.NewAssignOpStmt(g.pos(stmt), op, g.expr(stmt.Lhs), g.expr(stmt.Rhs))
                        }
-                       return typecheck.Stmt(ir.NewAssignOpStmt(g.pos(stmt), op, g.expr(stmt.Lhs), g.expr(stmt.Rhs)))
+                       if n.X.Typecheck() == 3 {
+                               n.SetTypecheck(3)
+                               return n
+                       }
+                       transformAsOp(n)
+                       n.SetTypecheck(1)
+                       return n
                }
 
                names, lhs := g.assignList(stmt.Lhs, stmt.Op == syntax.Def)
                rhs := g.exprList(stmt.Rhs)
 
+               // We must delay transforming the assign statement if any of the
+               // lhs or rhs nodes are also delayed, since transformAssign needs
+               // to know the types of the left and right sides in various cases.
+               delay := false
+               for _, e := range lhs {
+                       if e.Typecheck() == 3 {
+                               delay = true
+                               break
+                       }
+               }
+               for _, e := range rhs {
+                       if e.Typecheck() == 3 {
+                               delay = true
+                               break
+                       }
+               }
+
                if len(lhs) == 1 && len(rhs) == 1 {
                        n := ir.NewAssignStmt(g.pos(stmt), lhs[0], rhs[0])
                        n.Def = initDefn(n, names)
-                       // Need to set Assigned in checkassign for maps
-                       return typecheck.Stmt(n)
+
+                       if delay {
+                               n.SetTypecheck(3)
+                               return n
+                       }
+
+                       lhs, rhs := []ir.Node{n.X}, []ir.Node{n.Y}
+                       transformAssign(n, lhs, rhs)
+                       n.X, n.Y = lhs[0], rhs[0]
+                       n.SetTypecheck(1)
+                       return n
                }
 
                n := ir.NewAssignListStmt(g.pos(stmt), ir.OAS2, lhs, rhs)
                n.Def = initDefn(n, names)
-               // Need to do tcAssignList().
-               return typecheck.Stmt(n)
+               if delay {
+                       n.SetTypecheck(3)
+                       return n
+               }
+               transformAssign(n, n.Lhs, n.Rhs)
+               n.SetTypecheck(1)
+               return n
 
        case *syntax.BranchStmt:
                return ir.NewBranchStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), branchOps[:]), g.name(stmt.Label))
@@ -87,16 +118,18 @@ func (g *irgen) stmt0(stmt syntax.Stmt) ir.Node {
                return ir.NewGoDeferStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), callOps[:]), g.expr(stmt.Call))
        case *syntax.ReturnStmt:
                n := ir.NewReturnStmt(g.pos(stmt), g.exprList(stmt.Results))
-               // Need to do typecheckaste() for multiple return values
-               return typecheck.Stmt(n)
+               transformReturn(n)
+               n.SetTypecheck(1)
+               return n
        case *syntax.IfStmt:
                return g.ifStmt(stmt)
        case *syntax.ForStmt:
                return g.forStmt(stmt)
        case *syntax.SelectStmt:
                n := g.selectStmt(stmt)
-               // Need to convert assignments to OSELRECV2 in tcSelect()
-               return typecheck.Stmt(n)
+               transformSelect(n.(*ir.SelectStmt))
+               n.SetTypecheck(1)
+               return n
        case *syntax.SwitchStmt:
                return g.switchStmt(stmt)
 
diff --git a/src/cmd/compile/internal/noder/transform.go b/src/cmd/compile/internal/noder/transform.go
new file mode 100644 (file)
index 0000000..e90d374
--- /dev/null
@@ -0,0 +1,523 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains transformation functions on nodes, which are the
+// transformations that the typecheck package does that are distinct from the
+// typechecking functionality. These transform functions are pared-down copies of
+// the original typechecking functions, with all code removed that is related to:
+//
+//    - Detecting compile-time errors (already done by types2)
+//    - Setting the actual type of existing nodes (already done based on
+//      type info from types2)
+//    - Dealing with untyped constants (which types2 has already resolved)
+
+package noder
+
+import (
+       "cmd/compile/internal/base"
+       "cmd/compile/internal/ir"
+       "cmd/compile/internal/typecheck"
+       "cmd/compile/internal/types"
+       "go/constant"
+)
+
+// Transformation functions for expressions
+
+// transformAdd transforms an addition operation (currently just addition of
+// strings). Corresponds to the "binary operators" case in typecheck.typecheck1.
+func transformAdd(n *ir.BinaryExpr) ir.Node {
+       l := n.X
+       if l.Type().IsString() {
+               var add *ir.AddStringExpr
+               if l.Op() == ir.OADDSTR {
+                       add = l.(*ir.AddStringExpr)
+                       add.SetPos(n.Pos())
+               } else {
+                       add = ir.NewAddStringExpr(n.Pos(), []ir.Node{l})
+               }
+               r := n.Y
+               if r.Op() == ir.OADDSTR {
+                       r := r.(*ir.AddStringExpr)
+                       add.List.Append(r.List.Take()...)
+               } else {
+                       add.List.Append(r)
+               }
+               add.SetType(l.Type())
+               return add
+       }
+       return n
+}
+
+// Corresponds to typecheck.stringtoruneslit.
+func stringtoruneslit(n *ir.ConvExpr) ir.Node {
+       if n.X.Op() != ir.OLITERAL || n.X.Val().Kind() != constant.String {
+               base.Fatalf("stringtoarraylit %v", n)
+       }
+
+       var l []ir.Node
+       i := 0
+       for _, r := range ir.StringVal(n.X) {
+               l = append(l, ir.NewKeyExpr(base.Pos, ir.NewInt(int64(i)), ir.NewInt(int64(r))))
+               i++
+       }
+
+       nn := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(n.Type()), nil)
+       nn.List = l
+       // Need to transform the OCOMPLIT.
+       // TODO(danscales): update this when we have written transformCompLit()
+       return typecheck.Expr(nn)
+}
+
+// transformConv transforms an OCONV node as needed, based on the types involved,
+// etc.  Corresponds to typecheck.tcConv.
+func transformConv(n *ir.ConvExpr) ir.Node {
+       t := n.X.Type()
+       op, _ := typecheck.Convertop(n.X.Op() == ir.OLITERAL, t, n.Type())
+       assert(op != ir.OXXX)
+       n.SetOp(op)
+       switch n.Op() {
+       case ir.OCONVNOP:
+               if t.Kind() == n.Type().Kind() {
+                       switch t.Kind() {
+                       case types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128:
+                               // Floating point casts imply rounding and
+                               // so the conversion must be kept.
+                               n.SetOp(ir.OCONV)
+                       }
+               }
+
+       // Do not convert to []byte literal. See CL 125796.
+       // Generated code and compiler memory footprint is better without it.
+       case ir.OSTR2BYTES:
+               // ok
+
+       case ir.OSTR2RUNES:
+               if n.X.Op() == ir.OLITERAL {
+                       return stringtoruneslit(n)
+               }
+       }
+       return n
+}
+
+// transformConvCall transforms a conversion call. Corresponds to the OTYPE part of
+// typecheck.tcCall.
+func transformConvCall(n *ir.CallExpr) ir.Node {
+       arg := n.Args[0]
+       n1 := ir.NewConvExpr(n.Pos(), ir.OCONV, nil, arg)
+       n1.SetType(n.X.Type())
+       return transformConv(n1)
+}
+
+// transformCall transforms a normal function/method call. Corresponds to last half
+// (non-conversion, non-builtin part) of typecheck.tcCall.
+func transformCall(n *ir.CallExpr) {
+       transformArgs(n)
+       l := n.X
+       t := l.Type()
+
+       switch l.Op() {
+       case ir.ODOTINTER:
+               n.SetOp(ir.OCALLINTER)
+
+       case ir.ODOTMETH:
+               l := l.(*ir.SelectorExpr)
+               n.SetOp(ir.OCALLMETH)
+
+               tp := t.Recv().Type
+
+               if l.X == nil || !types.Identical(l.X.Type(), tp) {
+                       base.Fatalf("method receiver")
+               }
+
+       default:
+               n.SetOp(ir.OCALLFUNC)
+       }
+
+       typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args)
+       if t.NumResults() == 0 {
+               return
+       }
+       if t.NumResults() == 1 {
+               n.SetType(l.Type().Results().Field(0).Type)
+
+               if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.ONAME {
+                       if sym := n.X.(*ir.Name).Sym(); types.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
+       }
+}
+
+// transformCompare transforms a compare operation (currently just equals/not
+// equals). Corresponds to the "comparison operators" case in
+// typecheck.typecheck1, including tcArith.
+func transformCompare(n *ir.BinaryExpr) {
+       if (n.Op() == ir.OEQ || n.Op() == ir.ONE) && !types.Identical(n.X.Type(), n.Y.Type()) {
+               // Comparison is okay as long as one side is assignable to the
+               // other. The only allowed case where the conversion is not CONVNOP is
+               // "concrete == interface". In that case, check comparability of
+               // the concrete type. The conversion allocates, so only do it if
+               // the concrete type is huge.
+               l, r := n.X, n.Y
+               lt, rt := l.Type(), r.Type()
+               converted := false
+               if rt.Kind() != types.TBLANK {
+                       aop, _ := typecheck.Assignop(lt, rt)
+                       if aop != ir.OXXX {
+                               types.CalcSize(lt)
+                               if rt.IsInterface() == lt.IsInterface() || lt.Width >= 1<<16 {
+                                       l = ir.NewConvExpr(base.Pos, aop, rt, l)
+                                       l.SetTypecheck(1)
+                               }
+
+                               converted = true
+                       }
+               }
+
+               if !converted && lt.Kind() != types.TBLANK {
+                       aop, _ := typecheck.Assignop(rt, lt)
+                       if aop != ir.OXXX {
+                               types.CalcSize(rt)
+                               if rt.IsInterface() == lt.IsInterface() || rt.Width >= 1<<16 {
+                                       r = ir.NewConvExpr(base.Pos, aop, lt, r)
+                                       r.SetTypecheck(1)
+                               }
+                       }
+               }
+               n.X, n.Y = l, r
+       }
+}
+
+// Corresponds to typecheck.implicitstar.
+func implicitstar(n ir.Node) ir.Node {
+       // insert implicit * if needed for fixed array
+       t := n.Type()
+       if !t.IsPtr() {
+               return n
+       }
+       t = t.Elem()
+       if !t.IsArray() {
+               return n
+       }
+       star := ir.NewStarExpr(base.Pos, n)
+       star.SetImplicit(true)
+       return typed(t, star)
+}
+
+// transformIndex transforms an index operation.  Corresponds to typecheck.tcIndex.
+func transformIndex(n *ir.IndexExpr) {
+       n.X = implicitstar(n.X)
+       l := n.X
+       t := l.Type()
+       if t.Kind() == types.TMAP {
+               n.Index = typecheck.AssignConv(n.Index, t.Key(), "map index")
+               n.SetOp(ir.OINDEXMAP)
+               // Set type to just the map value, not (value, bool). This is
+               // different from types2, but fits the later stages of the
+               // compiler better.
+               n.SetType(t.Elem())
+               n.Assigned = false
+       }
+}
+
+// transformSlice transforms a slice operation.  Corresponds to typecheck.tcSlice.
+func transformSlice(n *ir.SliceExpr) {
+       l := n.X
+       if l.Type().IsArray() {
+               addr := typecheck.NodAddr(n.X)
+               addr.SetImplicit(true)
+               typed(types.NewPtr(n.X.Type()), addr)
+               n.X = addr
+               l = addr
+       }
+       t := l.Type()
+       if t.IsString() {
+               n.SetOp(ir.OSLICESTR)
+       } else if t.IsPtr() && t.Elem().IsArray() {
+               if n.Op().IsSlice3() {
+                       n.SetOp(ir.OSLICE3ARR)
+               } else {
+                       n.SetOp(ir.OSLICEARR)
+               }
+       }
+}
+
+// Transformation functions for statements
+
+// Corresponds to typecheck.checkassign.
+func transformCheckAssign(stmt ir.Node, n ir.Node) {
+       if n.Op() == ir.OINDEXMAP {
+               n := n.(*ir.IndexExpr)
+               n.Assigned = true
+               return
+       }
+}
+
+// Corresponds to typecheck.assign.
+func transformAssign(stmt ir.Node, lhs, rhs []ir.Node) {
+       checkLHS := func(i int, typ *types.Type) {
+               transformCheckAssign(stmt, lhs[i])
+       }
+
+       cr := len(rhs)
+       if len(rhs) == 1 {
+               if rtyp := rhs[0].Type(); rtyp != nil && rtyp.IsFuncArgStruct() {
+                       cr = rtyp.NumFields()
+               }
+       }
+
+       // x, ok = y
+assignOK:
+       for len(lhs) == 2 && cr == 1 {
+               stmt := stmt.(*ir.AssignListStmt)
+               r := rhs[0]
+
+               switch r.Op() {
+               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
+               }
+               checkLHS(0, r.Type())
+               checkLHS(1, types.UntypedBool)
+               return
+       }
+
+       if len(lhs) != cr {
+               for i := range lhs {
+                       checkLHS(i, nil)
+               }
+               return
+       }
+
+       // 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 {
+                       checkLHS(i, rtyp.Field(i).Type)
+               }
+               return
+       }
+
+       for i, r := range rhs {
+               checkLHS(i, r.Type())
+               if lhs[i].Type() != nil {
+                       rhs[i] = assignconvfn(r, lhs[i].Type())
+               }
+       }
+}
+
+// Corresponds to typecheck.typecheckargs.
+func transformArgs(n ir.InitNode) {
+       var list []ir.Node
+       switch n := n.(type) {
+       default:
+               base.Fatalf("typecheckargs %+v", n.Op())
+       case *ir.CallExpr:
+               list = n.Args
+               if n.IsDDD {
+                       return
+               }
+       case *ir.ReturnStmt:
+               list = n.Results
+       }
+       if len(list) != 1 {
+               return
+       }
+
+       t := list[0].Type()
+       if t == nil || !t.IsFuncArgStruct() {
+               return
+       }
+
+       // Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...).
+
+       // Save n as n.Orig for fmt.go.
+       if ir.Orig(n) == n {
+               n.(ir.OrigNode).SetOrig(ir.SepCopy(n))
+       }
+
+       as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
+       as.Rhs.Append(list...)
+
+       // If we're outside of function context, then this call will
+       // be executed during the generated init function. However,
+       // init.go hasn't yet created it. Instead, associate the
+       // temporary variables with  InitTodoFunc for now, and init.go
+       // will reassociate them later when it's appropriate.
+       static := ir.CurFunc == nil
+       if static {
+               ir.CurFunc = typecheck.InitTodoFunc
+       }
+       list = nil
+       for _, f := range t.FieldSlice() {
+               t := typecheck.Temp(f.Type)
+               as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, t))
+               as.Lhs.Append(t)
+               list = append(list, t)
+       }
+       if static {
+               ir.CurFunc = nil
+       }
+
+       switch n := n.(type) {
+       case *ir.CallExpr:
+               n.Args = list
+       case *ir.ReturnStmt:
+               n.Results = list
+       }
+
+       transformAssign(as, as.Lhs, as.Rhs)
+       as.SetTypecheck(1)
+       n.PtrInit().Append(as)
+}
+
+// assignconvfn converts node n for assignment to type t. Corresponds to
+// typecheck.assignconvfn.
+func assignconvfn(n ir.Node, t *types.Type) ir.Node {
+       if t.Kind() == types.TBLANK {
+               return n
+       }
+
+       if types.Identical(n.Type(), t) {
+               return n
+       }
+
+       op, _ := typecheck.Assignop(n.Type(), t)
+
+       r := ir.NewConvExpr(base.Pos, op, t, n)
+       r.SetTypecheck(1)
+       r.SetImplicit(true)
+       return r
+}
+
+// Corresponds to typecheck.typecheckaste.
+func typecheckaste(op ir.Op, call ir.Node, isddd bool, tstruct *types.Type, nl ir.Nodes) {
+       var t *types.Type
+       var i int
+
+       lno := base.Pos
+       defer func() { base.Pos = lno }()
+
+       var n ir.Node
+       if len(nl) == 1 {
+               n = nl[0]
+       }
+
+       i = 0
+       for _, tl := range tstruct.Fields().Slice() {
+               t = tl.Type
+               if tl.IsDDD() {
+                       if isddd {
+                               n = nl[i]
+                               ir.SetPos(n)
+                               if n.Type() != nil {
+                                       nl[i] = assignconvfn(n, t)
+                               }
+                               return
+                       }
+
+                       // TODO(mdempsky): Make into ... call with implicit slice.
+                       for ; i < len(nl); i++ {
+                               n = nl[i]
+                               ir.SetPos(n)
+                               if n.Type() != nil {
+                                       nl[i] = assignconvfn(n, t.Elem())
+                               }
+                       }
+                       return
+               }
+
+               n = nl[i]
+               ir.SetPos(n)
+               if n.Type() != nil {
+                       nl[i] = assignconvfn(n, t)
+               }
+               i++
+       }
+}
+
+// transformSend transforms a send statement, converting the value to appropriate
+// type for the channel, as needed. Corresponds of typecheck.tcSend.
+func transformSend(n *ir.SendStmt) {
+       n.Value = assignconvfn(n.Value, n.Chan.Type().Elem())
+}
+
+// transformReturn transforms a return node, by doing the needed assignments and
+// any necessary conversions. Corresponds to typecheck.tcReturn()
+func transformReturn(rs *ir.ReturnStmt) {
+       transformArgs(rs)
+       nl := rs.Results
+       if ir.HasNamedResults(ir.CurFunc) && len(nl) == 0 {
+               return
+       }
+
+       typecheckaste(ir.ORETURN, nil, false, ir.CurFunc.Type().Results(), nl)
+}
+
+// transformSelect transforms a select node, creating an assignment list as needed
+// for each case. Corresponds to typecheck.tcSelect().
+func transformSelect(sel *ir.SelectStmt) {
+       for _, ncase := range sel.Cases {
+               if ncase.Comm != nil {
+                       n := ncase.Comm
+                       oselrecv2 := func(dst, recv ir.Node, def bool) {
+                               n := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv})
+                               n.Def = def
+                               n.SetTypecheck(1)
+                               ncase.Comm = n
+                       }
+                       switch n.Op() {
+                       case ir.OAS:
+                               // convert x = <-c into x, _ = <-c
+                               // remove implicit conversions; the eventual assignment
+                               // will reintroduce them.
+                               n := n.(*ir.AssignStmt)
+                               if r := n.Y; r.Op() == ir.OCONVNOP || r.Op() == ir.OCONVIFACE {
+                                       r := r.(*ir.ConvExpr)
+                                       if r.Implicit() {
+                                               n.Y = r.X
+                                       }
+                               }
+                               oselrecv2(n.X, n.Y, n.Def)
+
+                       case ir.OAS2RECV:
+                               n := n.(*ir.AssignListStmt)
+                               n.SetOp(ir.OSELRECV2)
+
+                       case ir.ORECV:
+                               // convert <-c into _, _ = <-c
+                               n := n.(*ir.UnaryExpr)
+                               oselrecv2(ir.BlankNode, n, false)
+
+                       case ir.OSEND:
+                               break
+                       }
+               }
+       }
+}
+
+// transformAsOp transforms an AssignOp statement. Corresponds to OASOP case in
+// typecheck1.
+func transformAsOp(n *ir.AssignOpStmt) {
+       transformCheckAssign(n, n.X)
+}
index 3341de8e04f95983bd51d5742ae31f773c6f437b..b926222c89c417ccf7a691f3fc9fe60a0c68e1ae 100644 (file)
@@ -23,10 +23,14 @@ func (g *irgen) match(t1 *types.Type, t2 types2.Type, hasOK bool) bool {
        }
 
        if hasOK {
-               // For has-ok values, types2 represents the expression's type as
-               // a 2-element tuple, whereas ir just uses the first type and
-               // infers that the second type is boolean.
-               return tuple.Len() == 2 && types.Identical(t1, g.typ(tuple.At(0).Type()))
+               // For has-ok values, types2 represents the expression's type as a
+               // 2-element tuple, whereas ir just uses the first type and infers
+               // that the second type is boolean. Must match either, since we
+               // sometimes delay the transformation to the ir form.
+               if tuple.Len() == 2 && types.Identical(t1, g.typ(tuple.At(0).Type())) {
+                       return true
+               }
+               return types.Identical(t1, g.typ(t2))
        }
 
        if t1 == nil || tuple == nil {
index fb39709686b7e58d5a405d97a8456249c85e4699..7ab1670a4517b82158c73105852c3b09b38b97ba 100644 (file)
@@ -419,7 +419,7 @@ func tcConv(n *ir.ConvExpr) ir.Node {
                n.SetType(nil)
                return n
        }
-       op, why := convertop(n.X.Op() == ir.OLITERAL, t, n.Type())
+       op, why := Convertop(n.X.Op() == ir.OLITERAL, t, n.Type())
        if op == ir.OXXX {
                if !n.Diag() && !n.Type().Broke() && !n.X.Diag() {
                        base.Errorf("cannot convert %L to type %v%s", n.X, n.Type(), why)
index e58ef9fb050c06fd274997050347875a9374b144..daf5cd72a27ebe137df78819109e2286fb3d51cc 100644 (file)
@@ -460,7 +460,7 @@ func Assignop(src, dst *types.Type) (ir.Op, string) {
 // If not, return OXXX. In this case, the string return parameter may
 // hold a reason why. In all other cases, it'll be the empty string.
 // srcConstant indicates whether the value of type src is a constant.
-func convertop(srcConstant bool, src, dst *types.Type) (ir.Op, string) {
+func Convertop(srcConstant bool, src, dst *types.Type) (ir.Op, string) {
        if src == dst {
                return ir.OCONVNOP, ""
        }