Skip to content

Commit 9f4d6a8

Browse files
committed
[dev.typeparams] cmd/compile: call transformArgs before early typecheckaste in noder
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 <[email protected]> Run-TryBot: Dan Scales <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent ca3c698 commit 9f4d6a8

File tree

5 files changed

+55
-91
lines changed

5 files changed

+55
-91
lines changed

src/cmd/compile/internal/noder/helpers.go

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -171,39 +171,34 @@ func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool)
171171
}
172172
}
173173

174-
if fun.Type().HasTParam() {
174+
if fun.Type().HasTParam() || fun.Op() == ir.OXDOT || fun.Op() == ir.OFUNCINST {
175175
// If the fun arg is or has a type param, we can't do all the
176-
// transformations, since we may not have needed properties yet.
177-
// (e.g. number of return values, etc). However, if we do have the
178-
// function type (even though it is parameterized), then can add in
179-
// any needed CONVIFACE nodes. We can't do anything if fun is a type
180-
// param (which is probably described by a structural constraint)
176+
// transformations, since we may not have needed properties yet
177+
// (e.g. number of return values, etc). The same applies if a fun
178+
// which is an XDOT could not be transformed yet because of a generic
179+
// type in the X of the selector expression.
180+
//
181+
// A function instantiation (even if fully concrete) shouldn't be
182+
// transformed yet, because we need to add the dictionary during the
183+
// transformation.
184+
//
185+
// However, if we have a function type (even though it is
186+
// parameterized), then we can add in any needed CONVIFACE nodes via
187+
// typecheckaste(). We need to call transformArgs() to deal first
188+
// with the f(g(()) case where g returns multiple return values. We
189+
// can't do anything if fun is a type param (which is probably
190+
// described by a structural constraint)
181191
if fun.Type().Kind() == types.TFUNC {
192+
transformArgs(n)
182193
typecheckaste(ir.OCALL, fun, n.IsDDD, fun.Type().Params(), n.Args, true)
183194
}
184195
return typed(typ, n)
185196
}
186197

187-
if fun.Op() == ir.OXDOT {
188-
if !fun.(*ir.SelectorExpr).X.Type().HasTParam() {
189-
base.FatalfAt(pos, "Expecting type param receiver in %v", fun)
190-
}
191-
// For methods called in a generic function, don't do any extra
192-
// transformations. We will do those later when we create the
193-
// instantiated function and have the correct receiver type.
194-
typed(typ, n)
195-
return n
196-
}
197-
if fun.Op() != ir.OFUNCINST {
198-
// If no type params, do the normal call transformations. This
199-
// will convert OCALL to OCALLFUNC.
200-
typed(typ, n)
201-
transformCall(n)
202-
return n
203-
}
204-
205-
// Leave the op as OCALL, which indicates the call still needs typechecking.
198+
// If no type params, do the normal call transformations. This
199+
// will convert OCALL to OCALLFUNC.
206200
typed(typ, n)
201+
transformCall(n)
207202
return n
208203
}
209204

src/cmd/compile/internal/noder/stencil.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,18 +161,21 @@ func (g *irgen) stencil() {
161161
}
162162
}
163163

164+
// Transform the Call now, which changes OCALL
165+
// to OCALLFUNC and does typecheckaste/assignconvfn.
166+
transformCall(call)
167+
164168
st := g.getInstantiation(gf, targs, true)
165169
dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, gf, targs, true)
166170
// We have to be using a subdictionary, since this is
167171
// a generic method call.
168172
assert(usingSubdict)
169173

170-
call.SetOp(ir.OCALL)
174+
// Transform to a function call, by appending the
175+
// dictionary and the receiver to the args.
176+
call.SetOp(ir.OCALLFUNC)
171177
call.X = st.Nname
172178
call.Args.Prepend(dictValue, meth.X)
173-
// Transform the Call now, which changes OCALL
174-
// to OCALLFUNC and does typecheckaste/assignconvfn.
175-
transformCall(call)
176179
modified = true
177180
}
178181
})

src/cmd/compile/internal/noder/stmt.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node {
129129
// Delay transforming the return statement if any of the
130130
// return values have a type param.
131131
if !ir.HasNamedResults(ir.CurFunc) {
132+
transformArgs(n)
132133
// But add CONVIFACE nodes where needed if
133134
// any of the return values have interface type.
134135
typecheckaste(ir.ORETURN, nil, false, ir.CurFunc.Type().Results(), n.Results, true)

src/cmd/compile/internal/noder/transform.go

Lines changed: 8 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ assignOK:
365365
}
366366
}
367367

368-
// Corresponds to, but slightly more general than, typecheck.typecheckargs.
368+
// Corresponds to typecheck.typecheckargs. Really just deals with multi-value calls.
369369
func transformArgs(n ir.InitNode) {
370370
var list []ir.Node
371371
switch n := n.(type) {
@@ -379,76 +379,22 @@ func transformArgs(n ir.InitNode) {
379379
case *ir.ReturnStmt:
380380
list = n.Results
381381
}
382-
383-
// Look to see if we have any multi-return functions as arguments.
384-
extra := 0
385-
for _, arg := range list {
386-
t := arg.Type()
387-
if t.IsFuncArgStruct() {
388-
num := t.Fields().Len()
389-
if num <= 1 {
390-
base.Fatalf("multi-return type with only %d parts", num)
391-
}
392-
extra += num - 1
393-
}
394-
}
395-
// If not, nothing to do.
396-
if extra == 0 {
382+
if len(list) != 1 {
397383
return
398384
}
399385

400-
// Rewrite f(..., g(), ...) into t1, ..., tN = g(); f(..., t1, ..., tN, ...).
386+
t := list[0].Type()
387+
if t == nil || !t.IsFuncArgStruct() {
388+
return
389+
}
401390

402391
// Save n as n.Orig for fmt.go.
403392
if ir.Orig(n) == n {
404393
n.(ir.OrigNode).SetOrig(ir.SepCopy(n))
405394
}
406395

407-
// If we're outside of function context, then this call will
408-
// be executed during the generated init function. However,
409-
// init.go hasn't yet created it. Instead, associate the
410-
// temporary variables with InitTodoFunc for now, and init.go
411-
// will reassociate them later when it's appropriate.
412-
static := ir.CurFunc == nil
413-
if static {
414-
ir.CurFunc = typecheck.InitTodoFunc
415-
}
416-
417-
// Expand multi-return function calls.
418-
// The spec only allows a multi-return function as an argument
419-
// if it is the only argument. This code must handle calls to
420-
// stenciled generic functions which have extra arguments
421-
// (like the dictionary) so it must handle a slightly more general
422-
// cases, like f(n, g()) where g is multi-return.
423-
newList := make([]ir.Node, 0, len(list)+extra)
424-
for _, arg := range list {
425-
t := arg.Type()
426-
if t.IsFuncArgStruct() {
427-
as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, []ir.Node{arg})
428-
for _, f := range t.FieldSlice() {
429-
t := typecheck.Temp(f.Type)
430-
as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, t))
431-
as.Lhs.Append(t)
432-
newList = append(newList, t)
433-
}
434-
transformAssign(as, as.Lhs, as.Rhs)
435-
as.SetTypecheck(1)
436-
n.PtrInit().Append(as)
437-
} else {
438-
newList = append(newList, arg)
439-
}
440-
}
441-
442-
if static {
443-
ir.CurFunc = nil
444-
}
445-
446-
switch n := n.(type) {
447-
case *ir.CallExpr:
448-
n.Args = newList
449-
case *ir.ReturnStmt:
450-
n.Results = newList
451-
}
396+
// Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...).
397+
typecheck.RewriteMultiValueCall(n, list[0])
452398
}
453399

454400
// assignconvfn converts node n for assignment to type t. Corresponds to

test/typeparam/issue47514b.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// run -gcflags=-G=3
2+
3+
// Copyright 2021 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package main
8+
9+
func Do[T any](do func() (T, string)) {
10+
_ = func() (T, string) {
11+
return do()
12+
}
13+
}
14+
15+
func main() {
16+
Do[int](func() (int, string) {
17+
return 3, "3"
18+
})
19+
}

0 commit comments

Comments
 (0)