diff options
Diffstat (limited to 'src/cmd/compile/internal/noder/transform.go')
-rw-r--r-- | src/cmd/compile/internal/noder/transform.go | 1091 |
1 files changed, 1091 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/noder/transform.go b/src/cmd/compile/internal/noder/transform.go new file mode 100644 index 0000000..db28e8d --- /dev/null +++ b/src/cmd/compile/internal/noder/transform.go @@ -0,0 +1,1091 @@ +// 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) +// +// Each of the transformation functions requires that node passed in has its type +// and typecheck flag set. If the transformation function replaces or adds new +// nodes, it will set the type and typecheck flag for those new nodes. + +package noder + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "fmt" + "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 { + assert(n.Type() != nil && n.Typecheck() == 1) + 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) + } + typed(l.Type(), add) + 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 list []ir.Node + i := 0 + eltType := n.Type().Elem() + for _, r := range ir.StringVal(n.X) { + elt := ir.NewKeyExpr(base.Pos, ir.NewInt(int64(i)), ir.NewInt(int64(r))) + // Change from untyped int to the actual element type determined + // by types2. No need to change elt.Key, since the array indexes + // are just used for setting up the element ordering. + elt.Value.SetType(eltType) + list = append(list, elt) + i++ + } + + nn := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(n.Type()), nil) + nn.List = list + typed(n.Type(), nn) + // Need to transform the OCOMPLIT. + return transformCompLit(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, why := typecheck.Convertop(n.X.Op() == ir.OLITERAL, t, n.Type()) + if op == ir.OXXX { + // types2 currently ignores pragmas, so a 'notinheap' mismatch is the + // one type-related error that it does not catch. This error will be + // caught here by Convertop (see two checks near beginning of + // Convertop) and reported at the end of noding. + base.ErrorfAt(n.Pos(), "cannot convert %L to type %v%s", n.X, n.Type(), why) + return n + } + 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) + } + + case ir.OBYTES2STR: + assert(t.IsSlice()) + assert(t.Elem().Kind() == types.TUINT8) + if t.Elem() != types.ByteType && t.Elem() != types.Types[types.TUINT8] { + // If t is a slice of a user-defined byte type B (not uint8 + // or byte), then add an extra CONVNOP from []B to []byte, so + // that the call to slicebytetostring() added in walk will + // typecheck correctly. + n.X = ir.NewConvExpr(n.X.Pos(), ir.OCONVNOP, types.NewSlice(types.ByteType), n.X) + n.X.SetTypecheck(1) + } + + case ir.ORUNES2STR: + assert(t.IsSlice()) + assert(t.Elem().Kind() == types.TINT32) + if t.Elem() != types.RuneType && t.Elem() != types.Types[types.TINT32] { + // If t is a slice of a user-defined rune type B (not uint32 + // or rune), then add an extra CONVNOP from []B to []rune, so + // that the call to slicerunetostring() added in walk will + // typecheck correctly. + n.X = ir.NewConvExpr(n.X.Pos(), ir.OCONVNOP, types.NewSlice(types.RuneType), n.X) + n.X.SetTypecheck(1) + } + + } + return n +} + +// transformConvCall transforms a conversion call. Corresponds to the OTYPE part of +// typecheck.tcCall. +func transformConvCall(n *ir.CallExpr) ir.Node { + assert(n.Type() != nil && n.Typecheck() == 1) + arg := n.Args[0] + n1 := ir.NewConvExpr(n.Pos(), ir.OCONV, nil, arg) + typed(n.X.Type(), n1) + return transformConv(n1) +} + +// transformCall transforms a normal function/method call. Corresponds to last half +// (non-conversion, non-builtin part) of typecheck.tcCall. This code should work even +// in the case of OCALL/OFUNCINST. +func transformCall(n *ir.CallExpr) { + // Set base.Pos, since transformArgs below may need it, but transformCall + // is called in some passes that don't set base.Pos. + ir.SetPos(n) + // n.Type() can be nil for calls with no return value + assert(n.Typecheck() == 1) + 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 l.Op() == ir.ODOTMETH && len(deref(n.X.Type().Recv().Type).RParams()) == 0 { + typecheck.FixMethodCall(n) + } + if t.NumResults() == 1 { + 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 + } +} + +// transformEarlyCall transforms the arguments of a call with an OFUNCINST node. +func transformEarlyCall(n *ir.CallExpr) { + transformArgs(n) + typecheckaste(ir.OCALL, n.X, n.IsDDD, n.X.Type().Params(), n.Args) +} + +// 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) { + assert(n.Type() != nil && n.Typecheck() == 1) + 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 lt.HasShape() || rt.IsInterface() == lt.IsInterface() || lt.Size() >= 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.HasShape() || rt.IsInterface() == lt.IsInterface() || rt.Size() >= 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) { + assert(n.Type() != nil && n.Typecheck() == 1) + n.X = implicitstar(n.X) + l := n.X + t := l.Type() + if t.Kind() == types.TMAP { + n.Index = assignconvfn(n.Index, t.Key()) + 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) { + assert(n.Type() != nil && n.Typecheck() == 1) + 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) + case ir.ODYNAMICDOTTYPE: + r := r.(*ir.DynamicTypeAssertExpr) + stmt.SetOp(ir.OAS2DOTTYPE) + r.SetOp(ir.ODYNAMICDOTTYPE2) + default: + break assignOK + } + checkLHS(0, r.Type()) + checkLHS(1, types.UntypedBool) + t := lhs[0].Type() + if t != nil && rhs[0].Type().HasShape() && t.IsInterface() && !types.IdenticalStrict(t, rhs[0].Type()) { + // This is a multi-value assignment (map, channel, or dot-type) + // where the main result is converted to an interface during the + // assignment. Normally, the needed CONVIFACE is not created + // until (*orderState).as2ok(), because the AS2* ops and their + // sub-ops are so tightly intertwined. But we need to create the + // CONVIFACE now to enable dictionary lookups. So, assign the + // results first to temps, so that we can manifest the CONVIFACE + // in assigning the first temp to lhs[0]. If we added the + // CONVIFACE into rhs[0] directly, we would break a lot of later + // code that depends on the tight coupling between the AS2* ops + // and their sub-ops. (Issue #50642). + v := typecheck.Temp(rhs[0].Type()) + ok := typecheck.Temp(types.Types[types.TBOOL]) + as := ir.NewAssignListStmt(base.Pos, stmt.Op(), []ir.Node{v, ok}, []ir.Node{r}) + as.Def = true + as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, v)) + as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, ok)) + as.SetTypecheck(1) + // Change stmt to be a normal assignment of the temps to the final + // left-hand-sides. We re-create the original multi-value assignment + // so that it assigns to the temps and add it as an init of stmt. + // + // TODO: fix the order of evaluation, so that the lval of lhs[0] + // is evaluated before rhs[0] (similar to problem in #50672). + stmt.SetOp(ir.OAS2) + stmt.PtrInit().Append(as) + // assignconvfn inserts the CONVIFACE. + stmt.Rhs = []ir.Node{assignconvfn(v, t), ok} + } + 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) + rtyp := r.Type() + + mismatched := false + failed := false + for i := range lhs { + result := rtyp.Field(i).Type + checkLHS(i, result) + + if lhs[i].Type() == nil || result == nil { + failed = true + } else if lhs[i] != ir.BlankNode && !types.Identical(lhs[i].Type(), result) { + mismatched = true + } + } + if mismatched && !failed { + typecheck.RewriteMultiValueCall(stmt, r) + } + 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. Really just deals with multi-value calls. +func transformArgs(n ir.InitNode) { + var list []ir.Node + switch n := n.(type) { + default: + base.Fatalf("transformArgs %+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 + } + + // Save n as n.Orig for fmt.go. + if ir.Orig(n) == n { + n.(ir.OrigNode).SetOrig(ir.SepCopy(n)) + } + + // Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...). + typecheck.RewriteMultiValueCall(n, list[0]) +} + +// 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 n.Op() == ir.OPAREN { + n = n.(*ir.ParenExpr).X + } + + if types.IdenticalStrict(n.Type(), t) { + return n + } + + op, why := Assignop(n.Type(), t) + if op == ir.OXXX { + base.Fatalf("found illegal assignment %+v -> %+v; %s", n.Type(), t, why) + } + + r := ir.NewConvExpr(base.Pos, op, t, n) + r.SetTypecheck(1) + r.SetImplicit(true) + return r +} + +func Assignop(src, dst *types.Type) (ir.Op, string) { + if src == dst { + return ir.OCONVNOP, "" + } + if src == nil || dst == nil || src.Kind() == types.TFORW || dst.Kind() == types.TFORW || src.Underlying() == nil || dst.Underlying() == nil { + return ir.OXXX, "" + } + + // 1. src type is identical to dst (taking shapes into account) + if types.Identical(src, dst) { + // We already know from assignconvfn above that IdenticalStrict(src, + // dst) is false, so the types are not exactly the same and one of + // src or dst is a shape. If dst is an interface (which means src is + // an interface too), we need a real OCONVIFACE op; otherwise we need a + // OCONVNOP. See issue #48453. + if dst.IsInterface() { + return ir.OCONVIFACE, "" + } else { + return ir.OCONVNOP, "" + } + } + return typecheck.Assignop1(src, dst) +} + +// Corresponds to typecheck.typecheckaste, but we add an extra flag convifaceOnly +// only. If convifaceOnly is true, we only do interface conversion. We use this to do +// early insertion of CONVIFACE nodes during noder2, when the function or args may +// have typeparams. +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) { + selrecv := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv}) + if dst.Op() == ir.ONAME && dst.(*ir.Name).Defn == n { + // Must fix Defn for dst, since we are + // completely changing the node. + dst.(*ir.Name).Defn = selrecv + } + selrecv.Def = def + selrecv.SetTypecheck(1) + selrecv.SetInit(n.Init()) + ncase.Comm = selrecv + } + 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) +} + +// transformDot transforms an OXDOT (or ODOT) or ODOT, ODOTPTR, ODOTMETH, +// ODOTINTER, or OMETHVALUE, as appropriate. It adds in extra nodes as needed to +// access embedded fields. Corresponds to typecheck.tcDot. +func transformDot(n *ir.SelectorExpr, isCall bool) ir.Node { + assert(n.Type() != nil && n.Typecheck() == 1) + if n.Op() == ir.OXDOT { + n = typecheck.AddImplicitDots(n) + n.SetOp(ir.ODOT) + + // Set the Selection field and typecheck flag for any new ODOT nodes + // added by AddImplicitDots(), and also transform to ODOTPTR if + // needed. Equivalent to 'n.X = typecheck(n.X, ctxExpr|ctxType)' in + // tcDot. + for n1 := n; n1.X.Op() == ir.ODOT; { + n1 = n1.X.(*ir.SelectorExpr) + if !n1.Implicit() { + break + } + t1 := n1.X.Type() + if t1.IsPtr() && !t1.Elem().IsInterface() { + t1 = t1.Elem() + n1.SetOp(ir.ODOTPTR) + } + typecheck.Lookdot(n1, t1, 0) + n1.SetTypecheck(1) + } + } + + t := n.X.Type() + + if n.X.Op() == ir.OTYPE { + return transformMethodExpr(n) + } + + if t.IsPtr() && !t.Elem().IsInterface() { + t = t.Elem() + n.SetOp(ir.ODOTPTR) + } + + f := typecheck.Lookdot(n, t, 0) + assert(f != nil) + + if (n.Op() == ir.ODOTINTER || n.Op() == ir.ODOTMETH) && !isCall { + n.SetOp(ir.OMETHVALUE) + // This converts a method type to a function type. See issue 47775. + n.SetType(typecheck.NewMethodType(n.Type(), nil)) + } + return n +} + +// Corresponds to typecheck.typecheckMethodExpr. +func transformMethodExpr(n *ir.SelectorExpr) (res ir.Node) { + t := n.X.Type() + + // Compute the method set for t. + var ms *types.Fields + if t.IsInterface() { + ms = t.AllMethods() + } else { + mt := types.ReceiverBaseType(t) + typecheck.CalcMethods(mt) + ms = mt.AllMethods() + + // The method expression T.m requires a wrapper when T + // is different from m's declared receiver type. We + // normally generate these wrappers while writing out + // runtime type descriptors, which is always done for + // types declared at package scope. However, we need + // to make sure to generate wrappers for anonymous + // receiver types too. + if mt.Sym() == nil { + typecheck.NeedRuntimeType(t) + } + } + + s := n.Sel + m := typecheck.Lookdot1(n, s, t, ms, 0) + if !t.HasShape() { + // It's OK to not find the method if t is instantiated by shape types, + // because we will use the methods on the generic type anyway. + assert(m != nil) + } + + n.SetOp(ir.OMETHEXPR) + n.Selection = m + n.SetType(typecheck.NewMethodType(m.Type, n.X.Type())) + return n +} + +// Corresponds to typecheck.tcAppend. +func transformAppend(n *ir.CallExpr) ir.Node { + transformArgs(n) + args := n.Args + t := args[0].Type() + assert(t.IsSlice()) + + if n.IsDDD { + if t.Elem().IsKind(types.TUINT8) && args[1].Type().IsString() { + return n + } + + args[1] = assignconvfn(args[1], t.Underlying()) + return n + } + + as := args[1:] + for i, n := range as { + assert(n.Type() != nil) + as[i] = assignconvfn(n, t.Elem()) + } + return n +} + +// Corresponds to typecheck.tcComplex. +func transformComplex(n *ir.BinaryExpr) ir.Node { + l := n.X + r := n.Y + + assert(types.Identical(l.Type(), r.Type())) + + var t *types.Type + switch l.Type().Kind() { + case types.TFLOAT32: + t = types.Types[types.TCOMPLEX64] + case types.TFLOAT64: + t = types.Types[types.TCOMPLEX128] + default: + panic(fmt.Sprintf("transformComplex: unexpected type %v", l.Type())) + } + + // Must set the type here for generics, because this can't be determined + // by substitution of the generic types. + typed(t, n) + return n +} + +// Corresponds to typecheck.tcDelete. +func transformDelete(n *ir.CallExpr) ir.Node { + transformArgs(n) + args := n.Args + assert(len(args) == 2) + + l := args[0] + r := args[1] + + args[1] = assignconvfn(r, l.Type().Key()) + return n +} + +// Corresponds to typecheck.tcMake. +func transformMake(n *ir.CallExpr) ir.Node { + args := n.Args + + n.Args = nil + l := args[0] + t := l.Type() + assert(t != nil) + + i := 1 + var nn ir.Node + switch t.Kind() { + case types.TSLICE: + l = args[i] + i++ + var r ir.Node + if i < len(args) { + r = args[i] + i++ + } + nn = ir.NewMakeExpr(n.Pos(), ir.OMAKESLICE, l, r) + + case types.TMAP: + if i < len(args) { + l = args[i] + i++ + } else { + l = ir.NewInt(0) + } + nn = ir.NewMakeExpr(n.Pos(), ir.OMAKEMAP, l, nil) + nn.SetEsc(n.Esc()) + + case types.TCHAN: + l = nil + if i < len(args) { + l = args[i] + i++ + } else { + l = ir.NewInt(0) + } + nn = ir.NewMakeExpr(n.Pos(), ir.OMAKECHAN, l, nil) + default: + panic(fmt.Sprintf("transformMake: unexpected type %v", t)) + } + + assert(i == len(args)) + typed(n.Type(), nn) + return nn +} + +// Corresponds to typecheck.tcPanic. +func transformPanic(n *ir.UnaryExpr) ir.Node { + n.X = assignconvfn(n.X, types.Types[types.TINTER]) + return n +} + +// Corresponds to typecheck.tcPrint. +func transformPrint(n *ir.CallExpr) ir.Node { + transformArgs(n) + return n +} + +// Corresponds to typecheck.tcRealImag. +func transformRealImag(n *ir.UnaryExpr) ir.Node { + l := n.X + var t *types.Type + + // Determine result type. + switch l.Type().Kind() { + case types.TCOMPLEX64: + t = types.Types[types.TFLOAT32] + case types.TCOMPLEX128: + t = types.Types[types.TFLOAT64] + default: + panic(fmt.Sprintf("transformRealImag: unexpected type %v", l.Type())) + } + + // Must set the type here for generics, because this can't be determined + // by substitution of the generic types. + typed(t, n) + return n +} + +// Corresponds to typecheck.tcLenCap. +func transformLenCap(n *ir.UnaryExpr) ir.Node { + n.X = implicitstar(n.X) + return n +} + +// Corresponds to Builtin part of tcCall. +func transformBuiltin(n *ir.CallExpr) ir.Node { + // n.Type() can be nil for builtins with no return value + assert(n.Typecheck() == 1) + fun := n.X.(*ir.Name) + op := fun.BuiltinOp + + switch op { + case ir.OAPPEND, ir.ODELETE, ir.OMAKE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER: + n.SetOp(op) + n.X = nil + switch op { + case ir.OAPPEND: + return transformAppend(n) + case ir.ODELETE: + return transformDelete(n) + case ir.OMAKE: + return transformMake(n) + case ir.OPRINT, ir.OPRINTN: + return transformPrint(n) + case ir.ORECOVER: + // nothing more to do + return n + } + + case ir.OCAP, ir.OCLOSE, ir.OIMAG, ir.OLEN, ir.OPANIC, ir.OREAL: + transformArgs(n) + fallthrough + + case ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: + u := ir.NewUnaryExpr(n.Pos(), op, n.Args[0]) + u1 := typed(n.Type(), ir.InitExpr(n.Init(), u)) // typecheckargs can add to old.Init + switch op { + case ir.OCAP, ir.OLEN: + return transformLenCap(u1.(*ir.UnaryExpr)) + case ir.OREAL, ir.OIMAG: + return transformRealImag(u1.(*ir.UnaryExpr)) + case ir.OPANIC: + return transformPanic(u1.(*ir.UnaryExpr)) + case ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: + // This corresponds to the EvalConst() call near end of typecheck(). + return typecheck.EvalConst(u1) + case ir.OCLOSE, ir.ONEW: + // nothing more to do + return u1 + } + + case ir.OCOMPLEX, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE: + transformArgs(n) + b := ir.NewBinaryExpr(n.Pos(), op, n.Args[0], n.Args[1]) + n1 := typed(n.Type(), ir.InitExpr(n.Init(), b)) + if op != ir.OCOMPLEX { + // nothing more to do + return n1 + } + return transformComplex(n1.(*ir.BinaryExpr)) + + default: + panic(fmt.Sprintf("transformBuiltin: unexpected op %v", op)) + } + + return n +} + +func hasKeys(l ir.Nodes) bool { + for _, n := range l { + if n.Op() == ir.OKEY || n.Op() == ir.OSTRUCTKEY { + return true + } + } + return false +} + +// transformArrayLit runs assignconvfn on each array element and returns the +// length of the slice/array that is needed to hold all the array keys/indexes +// (one more than the highest index). Corresponds to typecheck.typecheckarraylit. +func transformArrayLit(elemType *types.Type, bound int64, elts []ir.Node) int64 { + var key, length int64 + for i, elt := range elts { + ir.SetPos(elt) + r := elts[i] + var kv *ir.KeyExpr + if elt.Op() == ir.OKEY { + elt := elt.(*ir.KeyExpr) + key = typecheck.IndexConst(elt.Key) + assert(key >= 0) + kv = elt + r = elt.Value + } + + r = assignconvfn(r, elemType) + if kv != nil { + kv.Value = r + } else { + elts[i] = r + } + + key++ + if key > length { + length = key + } + } + + return length +} + +// transformCompLit transforms n to an OARRAYLIT, OSLICELIT, OMAPLIT, or +// OSTRUCTLIT node, with any needed conversions. Corresponds to +// typecheck.tcCompLit (and includes parts corresponding to tcStructLitKey). +func transformCompLit(n *ir.CompLitExpr) (res ir.Node) { + assert(n.Type() != nil && n.Typecheck() == 1) + lno := base.Pos + defer func() { + base.Pos = lno + }() + + // Save original node (including n.Right) + n.SetOrig(ir.Copy(n)) + + ir.SetPos(n) + + t := n.Type() + + switch t.Kind() { + default: + base.Fatalf("transformCompLit %v", t.Kind()) + + case types.TARRAY: + transformArrayLit(t.Elem(), t.NumElem(), n.List) + n.SetOp(ir.OARRAYLIT) + + case types.TSLICE: + length := transformArrayLit(t.Elem(), -1, n.List) + n.SetOp(ir.OSLICELIT) + n.Len = length + + case types.TMAP: + for _, l := range n.List { + ir.SetPos(l) + assert(l.Op() == ir.OKEY) + l := l.(*ir.KeyExpr) + + r := l.Key + l.Key = assignconvfn(r, t.Key()) + + r = l.Value + l.Value = assignconvfn(r, t.Elem()) + } + + n.SetOp(ir.OMAPLIT) + + case types.TSTRUCT: + // Need valid field offsets for Xoffset below. + types.CalcSize(t) + + if len(n.List) != 0 && !hasKeys(n.List) { + // simple list of values + ls := n.List + for i, n1 := range ls { + ir.SetPos(n1) + + f := t.Field(i) + n1 = assignconvfn(n1, f.Type) + ls[i] = ir.NewStructKeyExpr(base.Pos, f, n1) + } + assert(len(ls) >= t.NumFields()) + } else { + // keyed list + ls := n.List + for i, l := range ls { + ir.SetPos(l) + + kv := l.(*ir.KeyExpr) + key := kv.Key + + // Sym might have resolved to name in other top-level + // package, because of import dot. Redirect to correct sym + // before we do the lookup. + s := key.Sym() + if id, ok := key.(*ir.Ident); ok && typecheck.DotImportRefs[id] != nil { + s = typecheck.Lookup(s.Name) + } + if types.IsExported(s.Name) && s.Pkg != types.LocalPkg { + // Exported field names should always have + // local pkg. We only need to do this + // adjustment for generic functions that are + // being transformed after being imported + // from another package. + s = typecheck.Lookup(s.Name) + } + + // An OXDOT uses the Sym field to hold + // the field to the right of the dot, + // so s will be non-nil, but an OXDOT + // is never a valid struct literal key. + assert(!(s == nil || key.Op() == ir.OXDOT || s.IsBlank())) + + f := typecheck.Lookdot1(nil, s, t, t.Fields(), 0) + l := ir.NewStructKeyExpr(l.Pos(), f, kv.Value) + ls[i] = l + + l.Value = assignconvfn(l.Value, f.Type) + } + } + + n.SetOp(ir.OSTRUCTLIT) + } + + return n +} + +// transformAddr corresponds to typecheck.tcAddr. +func transformAddr(n *ir.AddrExpr) { + switch n.X.Op() { + case ir.OARRAYLIT, ir.OMAPLIT, ir.OSLICELIT, ir.OSTRUCTLIT: + n.SetOp(ir.OPTRLIT) + } +} |