]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile: call transformArgs before early typecheckaste in noder
authorDan Scales <danscales@google.com>
Fri, 6 Aug 2021 20:24:14 +0000 (13:24 -0700)
committerDan Scales <danscales@google.com>
Mon, 9 Aug 2021 19:43:09 +0000 (19:43 +0000)
In the cases where we do an early call to typecheckaste() in noder to
expose CONVIFACE nodes, we need a preceding call to transformArgs().
This is needed to allow typecheckaste() to run correctly, in the case of
f(g()), where g has multiple return values.

I also cleaned up the code a bit and commented the code in Call(), and
we do the call to typecheckaste() in several more cases.

In stencil.go:stencil(), I moved the transformCall earlier for the
OCALLMETH/ODOTMETH case, just as I did in my previous CL for
OCALL/OFUNCINST. By doing this, transformArgs no longer needs to deal
with the extra dictionary args. Therefore, I was able to simply
transformArgs() to look like typecheckargs() again, and make use of
RewriteMultiValue directly.

Updates #47514

Change-Id: I49eb82ac05707e50c2e2fb03e39458a70491d406
Reviewed-on: https://go-review.googlesource.com/c/go/+/340531
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@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
test/typeparam/issue47514b.go [new file with mode: 0644]

index 2b00a9d7a632d4786d241cdd12aa6698bab08524..b9dbd030af190a0c5d34cce1b9b02f2eda7b1a1b 100644 (file)
@@ -171,39 +171,34 @@ func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool)
                }
        }
 
-       if fun.Type().HasTParam() {
+       if fun.Type().HasTParam() || fun.Op() == ir.OXDOT || fun.Op() == ir.OFUNCINST {
                // If the fun arg is or has a type param, we can't do all the
-               // transformations, since we may not have needed properties yet.
-               // (e.g. number of return values, etc). However, if we do have the
-               // function type (even though it is parameterized), then can add in
-               // any needed CONVIFACE nodes. We can't do anything if fun is a type
-               // param (which is probably described by a structural constraint)
+               // transformations, since we may not have needed properties yet
+               // (e.g. number of return values, etc). The same applies if a fun
+               // which is an XDOT could not be transformed yet because of a generic
+               // type in the X of the selector expression.
+               //
+               // A function instantiation (even if fully concrete) shouldn't be
+               // transformed yet, because we need to add the dictionary during the
+               // transformation.
+               //
+               // However, if we have a function type (even though it is
+               // parameterized), then we can add in any needed CONVIFACE nodes via
+               // typecheckaste(). We need to call transformArgs() to deal first
+               // with the f(g(()) case where g returns multiple return values. We
+               // can't do anything if fun is a type param (which is probably
+               // described by a structural constraint)
                if fun.Type().Kind() == types.TFUNC {
+                       transformArgs(n)
                        typecheckaste(ir.OCALL, fun, n.IsDDD, fun.Type().Params(), n.Args, true)
                }
                return typed(typ, n)
        }
 
-       if fun.Op() == ir.OXDOT {
-               if !fun.(*ir.SelectorExpr).X.Type().HasTParam() {
-                       base.FatalfAt(pos, "Expecting type param receiver in %v", fun)
-               }
-               // For methods called in a generic function, don't do any extra
-               // transformations. We will do those later when we create the
-               // instantiated function and have the correct receiver type.
-               typed(typ, n)
-               return n
-       }
-       if fun.Op() != ir.OFUNCINST {
-               // If no type params, do the normal call transformations. This
-               // will convert OCALL to OCALLFUNC.
-               typed(typ, n)
-               transformCall(n)
-               return n
-       }
-
-       // Leave the op as OCALL, which indicates the call still needs typechecking.
+       // If no type params, do the normal call transformations. This
+       // will convert OCALL to OCALLFUNC.
        typed(typ, n)
+       transformCall(n)
        return n
 }
 
index 5f2250d2f4bea712ea68fea3f8385b0b36b3b322..23e8090136ff5084d5e5445de637af3865c83343 100644 (file)
@@ -161,18 +161,21 @@ func (g *irgen) stencil() {
                                        }
                                }
 
+                               // Transform the Call now, which changes OCALL
+                               // to OCALLFUNC and does typecheckaste/assignconvfn.
+                               transformCall(call)
+
                                st := g.getInstantiation(gf, targs, true)
                                dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, gf, targs, true)
                                // We have to be using a subdictionary, since this is
                                // a generic method call.
                                assert(usingSubdict)
 
-                               call.SetOp(ir.OCALL)
+                               // Transform to a function call, by appending the
+                               // dictionary and the receiver to the args.
+                               call.SetOp(ir.OCALLFUNC)
                                call.X = st.Nname
                                call.Args.Prepend(dictValue, meth.X)
-                               // Transform the Call now, which changes OCALL
-                               // to OCALLFUNC and does typecheckaste/assignconvfn.
-                               transformCall(call)
                                modified = true
                        }
                })
index 5af4a2da9c634d9e40c21007e7a66697e2f89f86..1949f56095ffb45891406dacc48e0eb07cbcc73d 100644 (file)
@@ -129,6 +129,7 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node {
                                // Delay transforming the return statement if any of the
                                // return values have a type param.
                                if !ir.HasNamedResults(ir.CurFunc) {
+                                       transformArgs(n)
                                        // But add CONVIFACE nodes where needed if
                                        // any of the return values have interface type.
                                        typecheckaste(ir.ORETURN, nil, false, ir.CurFunc.Type().Results(), n.Results, true)
index ff113877dfc34973e7cfbb54ba933d6fa2cd03d5..e1eeb8e73940fa1b9adb98b3c5db043e764c082a 100644 (file)
@@ -365,7 +365,7 @@ assignOK:
        }
 }
 
-// Corresponds to, but slightly more general than, typecheck.typecheckargs.
+// Corresponds to typecheck.typecheckargs.  Really just deals with multi-value calls.
 func transformArgs(n ir.InitNode) {
        var list []ir.Node
        switch n := n.(type) {
@@ -379,76 +379,22 @@ func transformArgs(n ir.InitNode) {
        case *ir.ReturnStmt:
                list = n.Results
        }
-
-       // Look to see if we have any multi-return functions as arguments.
-       extra := 0
-       for _, arg := range list {
-               t := arg.Type()
-               if t.IsFuncArgStruct() {
-                       num := t.Fields().Len()
-                       if num <= 1 {
-                               base.Fatalf("multi-return type with only %d parts", num)
-                       }
-                       extra += num - 1
-               }
-       }
-       // If not, nothing to do.
-       if extra == 0 {
+       if len(list) != 1 {
                return
        }
 
-       // Rewrite f(..., g(), ...) into t1, ..., tN = g(); f(..., t1, ..., tN, ...).
+       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))
        }
 
-       // 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
-       }
-
-       // Expand multi-return function calls.
-       // The spec only allows a multi-return function as an argument
-       // if it is the only argument. This code must handle calls to
-       // stenciled generic functions which have extra arguments
-       // (like the dictionary) so it must handle a slightly more general
-       // cases, like f(n, g()) where g is multi-return.
-       newList := make([]ir.Node, 0, len(list)+extra)
-       for _, arg := range list {
-               t := arg.Type()
-               if t.IsFuncArgStruct() {
-                       as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, []ir.Node{arg})
-                       for _, f := range t.FieldSlice() {
-                               t := typecheck.Temp(f.Type)
-                               as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, t))
-                               as.Lhs.Append(t)
-                               newList = append(newList, t)
-                       }
-                       transformAssign(as, as.Lhs, as.Rhs)
-                       as.SetTypecheck(1)
-                       n.PtrInit().Append(as)
-               } else {
-                       newList = append(newList, arg)
-               }
-       }
-
-       if static {
-               ir.CurFunc = nil
-       }
-
-       switch n := n.(type) {
-       case *ir.CallExpr:
-               n.Args = newList
-       case *ir.ReturnStmt:
-               n.Results = newList
-       }
+       // 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
diff --git a/test/typeparam/issue47514b.go b/test/typeparam/issue47514b.go
new file mode 100644 (file)
index 0000000..5428a0e
--- /dev/null
@@ -0,0 +1,19 @@
+// run -gcflags=-G=3
+
+// 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.
+
+package main
+
+func Do[T any](do func() (T, string)) {
+       _ = func() (T, string) {
+               return do()
+       }
+}
+
+func main() {
+       Do[int](func() (int, string) {
+               return 3, "3"
+       })
+}