1
0
mirror of https://github.com/golang/go synced 2024-11-26 09:18:07 -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 // generic F, not immediately called
closureRequired = true 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 // T.M, T a type which is generic, not immediately
// called. Not necessary if the method selected is // called. Not necessary if the method selected is
// actually for an embedded interface field. // 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()] outerInfo = g.instInfoMap[outer.Sym()]
} }
usingSubdict := false usingSubdict := false
valueMethod := false
if x.Op() == ir.OFUNCINST { if x.Op() == ir.OFUNCINST {
inst := x.(*ir.InstExpr) inst := x.(*ir.InstExpr)
@ -269,17 +270,11 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
} }
} else { // ir.OMETHEXPR } else { // ir.OMETHEXPR
// Method expression T.M where T is a generic type. // Method expression T.M where T is a generic type.
// TODO: Is (*T).M right?
se := x.(*ir.SelectorExpr) 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 { if len(targs) == 0 {
panic("bad") panic("bad")
} }
}
}
// se.X.Type() is the top-level type of the method expression. To // se.X.Type() is the top-level type of the method expression. To
// correctly handle method expressions involving embedded fields, // correctly handle method expressions involving embedded fields,
@ -295,6 +290,10 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
break 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) target = g.getInstantiation(gf, targs, true)
dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, true) dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, true)
if infoPrintMode { 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 // If we are doing a method expression, we need to
// explicitly traverse any embedded fields in the receiver // explicitly traverse any embedded fields in the receiver
// argument in order to call the method instantiation. // 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)) arg0 := formalParams[0].Nname.(ir.Node)
args = append(args, dot.X) 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 { } else {
args = append(args, formalParams[i].Nname.(*ir.Name)) 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 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 { func deref(t *types.Type) *types.Type {
if t.IsPtr() { if t.IsPtr() {
return t.Elem() return t.Elem()

View File

@ -1957,7 +1957,7 @@ func (w *exportWriter) expr(n ir.Node) {
w.typ(n.Type()) w.typ(n.Type())
// unary expressions // 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) n := n.(*ir.UnaryExpr)
w.op(n.Op()) w.op(n.Op())
w.pos(n.Pos()) w.pos(n.Pos())
@ -1993,7 +1993,7 @@ func (w *exportWriter) expr(n ir.Node) {
// binary expressions // binary expressions
case ir.OADD, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OEQ, ir.OGE, ir.OGT, ir.OLE, ir.OLT, 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) n := n.(*ir.BinaryExpr)
w.op(n.Op()) w.op(n.Op())
w.pos(n.Pos()) 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) return ir.NewLinksymOffsetExpr(pos, Lookup(name).Linksym(), int64(off), typ)
// unary expressions // 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()) n := ir.NewUnaryExpr(r.pos(), op, r.expr())
if go117ExportTypes { if go117ExportTypes {
n.SetType(r.typ()) n.SetType(r.typ())
@ -1521,7 +1521,7 @@ func (r *importReader) node() ir.Node {
// binary expressions // binary expressions
case ir.OADD, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OEQ, ir.OGE, ir.OGT, ir.OLE, ir.OLT, 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()) n := ir.NewBinaryExpr(r.pos(), op, r.expr(), r.expr())
if go117ExportTypes { if go117ExportTypes {
n.SetType(r.typ()) n.SetType(r.typ())

View File

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

View File

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