1
0
mirror of https://github.com/golang/go synced 2024-11-26 07:17:59 -07:00

[dev.typeparams] cmd/compile: handle the (*T).M method expression with dictionaries

The (*T).M method expression is where M is a value method, but the type
(*T) is a pointer to the main type. In this case, after following any
embedded fields, we need to add an extra star operator when using the
receiver arg in the closure call.

Thanks to Cuong for finding/pointing out an example for this case
(typeparam/mdempsky/14.go) This example also shows that we now need the
ability to export/import OEFACE and OIDATA, which I added.

Change-Id: Ida0f81ce757fff78fec6276c60052ed71d207454
Reviewed-on: https://go-review.googlesource.com/c/go/+/333014
Run-TryBot: Dan Scales <danscales@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
This commit is contained in:
Dan Scales 2021-07-06 10:53:00 -07:00
parent 4676c3675e
commit b4844c9f54
5 changed files with 34 additions and 17 deletions

View File

@ -94,7 +94,7 @@ func (g *irgen) stencil() {
// generic F, not immediately called
closureRequired = true
}
if n.Op() == ir.OMETHEXPR && len(n.(*ir.SelectorExpr).X.Type().RParams()) > 0 && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) {
if n.Op() == ir.OMETHEXPR && len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) {
// T.M, T a type which is generic, not immediately
// called. Not necessary if the method selected is
// actually for an embedded interface field.
@ -229,6 +229,7 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
outerInfo = g.instInfoMap[outer.Sym()]
}
usingSubdict := false
valueMethod := false
if x.Op() == ir.OFUNCINST {
inst := x.(*ir.InstExpr)
@ -269,16 +270,10 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
}
} else { // ir.OMETHEXPR
// Method expression T.M where T is a generic type.
// TODO: Is (*T).M right?
se := x.(*ir.SelectorExpr)
targs := se.X.Type().RParams()
targs := deref(se.X.Type()).RParams()
if len(targs) == 0 {
if se.X.Type().IsPtr() {
targs = se.X.Type().Elem().RParams()
if len(targs) == 0 {
panic("bad")
}
}
panic("bad")
}
// se.X.Type() is the top-level type of the method expression. To
@ -295,6 +290,10 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
break
}
}
if !gf.Type().Recv().Type.IsPtr() {
// Remember if value method, so we can detect (*T).M case.
valueMethod = true
}
target = g.getInstantiation(gf, targs, true)
dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, true)
if infoPrintMode {
@ -446,8 +445,15 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
// If we are doing a method expression, we need to
// explicitly traverse any embedded fields in the receiver
// argument in order to call the method instantiation.
dot := typecheck.AddImplicitDots(ir.NewSelectorExpr(base.Pos, ir.OXDOT, formalParams[0].Nname.(*ir.Name), x.(*ir.SelectorExpr).Sel))
args = append(args, dot.X)
arg0 := formalParams[0].Nname.(ir.Node)
arg0 = typecheck.AddImplicitDots(ir.NewSelectorExpr(base.Pos, ir.OXDOT, arg0, x.(*ir.SelectorExpr).Sel)).X
if valueMethod && arg0.Type().IsPtr() {
// For handling the (*T).M case: if we have a pointer
// receiver after following all the embedded fields,
// but it's a value method, add a star operator.
arg0 = ir.NewStarExpr(arg0.Pos(), arg0)
}
args = append(args, arg0)
} else {
args = append(args, formalParams[i].Nname.(*ir.Name))
}
@ -1342,7 +1348,7 @@ func (subst *subster) fields(class ir.Class, oldfields []*types.Field, dcl []*ir
return newfields
}
// defer does a single defer of type t, if it is a pointer type.
// deref does a single deref of type t, if it is a pointer type.
func deref(t *types.Type) *types.Type {
if t.IsPtr() {
return t.Elem()

View File

@ -1957,7 +1957,7 @@ func (w *exportWriter) expr(n ir.Node) {
w.typ(n.Type())
// unary expressions
case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV:
case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV, ir.OIDATA:
n := n.(*ir.UnaryExpr)
w.op(n.Op())
w.pos(n.Pos())
@ -1993,7 +1993,7 @@ func (w *exportWriter) expr(n ir.Node) {
// binary expressions
case ir.OADD, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OEQ, ir.OGE, ir.OGT, ir.OLE, ir.OLT,
ir.OLSH, ir.OMOD, ir.OMUL, ir.ONE, ir.OOR, ir.ORSH, ir.OSUB, ir.OXOR:
ir.OLSH, ir.OMOD, ir.OMUL, ir.ONE, ir.OOR, ir.ORSH, ir.OSUB, ir.OXOR, ir.OEFACE:
n := n.(*ir.BinaryExpr)
w.op(n.Op())
w.pos(n.Pos())

View File

@ -1497,7 +1497,7 @@ func (r *importReader) node() ir.Node {
return ir.NewLinksymOffsetExpr(pos, Lookup(name).Linksym(), int64(off), typ)
// unary expressions
case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV:
case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV, ir.OIDATA:
n := ir.NewUnaryExpr(r.pos(), op, r.expr())
if go117ExportTypes {
n.SetType(r.typ())
@ -1521,7 +1521,7 @@ func (r *importReader) node() ir.Node {
// binary expressions
case ir.OADD, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OEQ, ir.OGE, ir.OGT, ir.OLE, ir.OLT,
ir.OLSH, ir.OMOD, ir.OMUL, ir.ONE, ir.OOR, ir.ORSH, ir.OSUB, ir.OXOR:
ir.OLSH, ir.OMOD, ir.OMUL, ir.ONE, ir.OOR, ir.ORSH, ir.OSUB, ir.OXOR, ir.OEFACE:
n := ir.NewBinaryExpr(r.pos(), op, r.expr(), r.expr())
if go117ExportTypes {
n.SetType(r.typ())

View File

@ -2215,7 +2215,6 @@ var g3Failures = setOf(
"typeparam/mdempsky/11.go",
"typeparam/mdempsky/12.go",
"typeparam/mdempsky/13.go",
"typeparam/mdempsky/14.go",
)
var unifiedFailures = setOf(

View File

@ -73,20 +73,32 @@ func methodExpressions() {
x := s[int]{a:7}
f0 := s[int].g0
f0(x)
f0p := (*s[int]).g0
f0p(&x)
f1 := s[int].g1
is7(f1(x))
f1p := (*s[int]).g1
is7(f1p(&x))
f2 := s[int].g2
is77(f2(x))
f2p := (*s[int]).g2
is77(f2p(&x))
}
func genMethodExpressions[T comparable](want T) {
x := s[T]{a: want}
f0 := s[T].g0
f0(x)
f0p := (*s[T]).g0
f0p(&x)
f1 := s[T].g1
if got := f1(x); got != want {
panic(fmt.Sprintf("f1(x) == %d, want %d", got, want))
}
f1p := (*s[T]).g1
if got := f1p(&x); got != want {
panic(fmt.Sprintf("f1p(&x) == %d, want %d", got, want))
}
f2 := s[T].g2
if got1, got2 := f2(x); got1 != want || got2 != want {
panic(fmt.Sprintf("f2(x) == %d, %d, want %d, %d", got1, got2, want, want))