cmd/compile: delay all call transforms if in a generic function

We changed to delaying all transforms of generic functions, since there
are so many complicated situations where type params can be used. We
missed changing so that all Call expressions(not just some) are delayed
if in a generic function. This changes to delaying all transforms on
calls in generic functions. Had to convert Call() to g.callExpr() (so we
can access g.delayTransform()). By always delaying transforms on calls
in generic functions, we actually simplify the code a bit both in
g.CallExpr() and stencil.go.

Fixes #51236

Change-Id: I0342c7995254082c4baf709b0b92a06ec14425e9
Reviewed-on: https://go-review.googlesource.com/c/go/+/386220
Reviewed-by: Keith Randall <khr@golang.org>
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Dan Scales 2021-10-31 19:45:21 -07:00
parent 8c5904f149
commit 4edefe9568
4 changed files with 71 additions and 100 deletions

View file

@ -98,95 +98,6 @@ func Binary(pos src.XPos, op ir.Op, typ *types.Type, x, y ir.Node) *ir.BinaryExp
}
}
func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool) ir.Node {
n := ir.NewCallExpr(pos, ir.OCALL, fun, args)
n.IsDDD = dots
if fun.Op() == ir.OTYPE {
// Actually a type conversion, not a function call.
if !fun.Type().IsInterface() &&
(fun.Type().HasTParam() || args[0].Type().HasTParam()) {
// For type params, we can transform if fun.Type() is known
// to be an interface (in which case a CONVIFACE node will be
// inserted). Otherwise, don't typecheck until we actually
// know the type.
return typed(typ, n)
}
typed(typ, n)
return transformConvCall(n)
}
if fun, ok := fun.(*ir.Name); ok && fun.BuiltinOp != 0 {
// For most Builtin ops, we delay doing transformBuiltin if any of the
// args have type params, for a variety of reasons:
//
// OMAKE: transformMake can't choose specific ops OMAKESLICE, etc.
// until arg type is known
// OREAL/OIMAG: transformRealImag can't determine type float32/float64
// until arg type known
// OAPPEND: transformAppend requires that the arg is a slice
// ODELETE: transformDelete requires that the arg is a map
// OALIGNOF, OSIZEOF: can be eval'ed to a constant until types known.
switch fun.BuiltinOp {
case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.ODELETE, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
hasTParam := false
for _, arg := range args {
if fun.BuiltinOp == ir.OOFFSETOF {
// It's the type of left operand of the
// selection that matters, not the type of
// the field itself (which is irrelevant for
// offsetof).
arg = arg.(*ir.SelectorExpr).X
}
if arg.Type().HasTParam() {
hasTParam = true
break
}
}
if hasTParam {
return typed(typ, n)
}
}
typed(typ, n)
return transformBuiltin(n)
}
// Add information, now that we know that fun is actually being called.
switch fun := fun.(type) {
case *ir.SelectorExpr:
if fun.Op() == ir.OMETHVALUE {
op := ir.ODOTMETH
if fun.X.Type().IsInterface() {
op = ir.ODOTINTER
}
fun.SetOp(op)
// Set the type to include the receiver, since that's what
// later parts of the compiler expect
fun.SetType(fun.Selection.Type)
}
}
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). 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.
return typed(typ, n)
}
// If no type params, do the normal call transformations. This
// will convert OCALL to OCALLFUNC.
typed(typ, n)
transformCall(n)
return n
}
func Compare(pos src.XPos, typ *types.Type, op ir.Op, x, y ir.Node) *ir.BinaryExpr {
n := ir.NewBinaryExpr(pos, op, x, y)
typed(typ, n)