[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>
diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go
index dbaebf7..656cab8 100644
--- a/src/cmd/compile/internal/noder/stencil.go
+++ b/src/cmd/compile/internal/noder/stencil.go
@@ -94,7 +94,7 @@
 				// 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 @@
 		outerInfo = g.instInfoMap[outer.Sym()]
 	}
 	usingSubdict := false
+	valueMethod := false
 	if x.Op() == ir.OFUNCINST {
 		inst := x.(*ir.InstExpr)
 
@@ -269,16 +270,10 @@
 		}
 	} 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 @@
 				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 @@
 			// 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 @@
 	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()
diff --git a/src/cmd/compile/internal/typecheck/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go
index 82bbda5..b717c37 100644
--- a/src/cmd/compile/internal/typecheck/iexport.go
+++ b/src/cmd/compile/internal/typecheck/iexport.go
@@ -1957,7 +1957,7 @@
 		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 @@
 
 	// 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())
diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go
index 17e60ef..f178869 100644
--- a/src/cmd/compile/internal/typecheck/iimport.go
+++ b/src/cmd/compile/internal/typecheck/iimport.go
@@ -1497,7 +1497,7 @@
 		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 @@
 
 	// 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())
diff --git a/test/run.go b/test/run.go
index df3befb..7afad0e 100644
--- a/test/run.go
+++ b/test/run.go
@@ -2215,7 +2215,6 @@
 	"typeparam/mdempsky/11.go",
 	"typeparam/mdempsky/12.go",
 	"typeparam/mdempsky/13.go",
-	"typeparam/mdempsky/14.go",
 )
 
 var unifiedFailures = setOf(
diff --git a/test/typeparam/dictionaryCapture.go b/test/typeparam/dictionaryCapture.go
index af50885..26af7a0 100644
--- a/test/typeparam/dictionaryCapture.go
+++ b/test/typeparam/dictionaryCapture.go
@@ -73,20 +73,32 @@
 	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))