aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/noder
diff options
context:
space:
mode:
authorMark Freeman <mark@golang.org>2026-03-30 13:09:06 -0400
committerGopher Robot <gobot@golang.org>2026-03-31 15:05:08 -0700
commitcafe443251e5c0bd95849a0bd18c014530f3118c (patch)
tree4752b8421f30e93ff3be9d32a354c5cdad7e9fbc /src/cmd/compile/internal/noder
parentfdd485c5f1701dfadb3a74d95c606329ec6463aa (diff)
downloadgo-cafe443251e5c0bd95849a0bd18c014530f3118c.tar.xz
cmd/compile/internal/noder: method expressions for generic methods
This change permits shaping method expressions for generic methods. The API is slightly different for generic and non-generic methods, as explained in code comments. Using OMETHEXPR minimizes the necessary changes, but forces us to split / rejoin the linker symbol for generic methods. While a bit odd, it seems sound. Change-Id: Iff28b9b11b9e83f450225aba0873644633f20633 Reviewed-on: https://go-review.googlesource.com/c/go/+/761220 Reviewed-by: Robert Griesemer <gri@google.com> Auto-Submit: Mark Freeman <markfreeman@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/cmd/compile/internal/noder')
-rw-r--r--src/cmd/compile/internal/noder/reader.go90
1 files changed, 68 insertions, 22 deletions
diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go
index e4f2a39acb..3fc6a659d9 100644
--- a/src/cmd/compile/internal/noder/reader.go
+++ b/src/cmd/compile/internal/noder/reader.go
@@ -1368,13 +1368,12 @@ func (r *reader) callShaped(pos src.XPos) {
var shapedFn ir.Node
if r.methodSym == nil {
- // Instantiating a generic function; shapedObj is the shaped
- // function itself.
+ // Instantiating a generic function; shapedObj is the shaped function itself.
assert(shapedObj.Op() == ir.ONAME && shapedObj.Class == ir.PFUNC)
shapedFn = shapedObj
} else {
- // Instantiating a generic type's method; shapedObj is the shaped
- // type, so we need to select it's corresponding method.
+ // Instantiating a generic type's method; shapedObj is the shaped method itself
+ // if the method is generic — else, it is the shaped type declaring the method.
shapedFn = shapedMethodExpr(pos, shapedObj, r.methodSym)
}
@@ -2929,28 +2928,75 @@ func (r *reader) methodExpr() (wrapperFn, baseFn, dictPtr ir.Node) {
return fn, fn, nil
}
-// shapedMethodExpr returns the specified method on the given shaped
-// type.
-func shapedMethodExpr(pos src.XPos, obj *ir.Name, sym *types.Sym) *ir.SelectorExpr {
- assert(obj.Op() == ir.OTYPE)
-
- typ := obj.Type()
- assert(typ.HasShape())
+// shapedMethodExpr creates an OMETHEXPR for obj using sym.
+//
+// If obj is an OTYPE, it must refer to a generic type. If obj is an ONAME,
+// it must refer to a generic method. In either case, sym.Name must be the
+// unqualified name of the method.
+//
+// For example, given:
+//
+// package p
+//
+// type T[P any] struct {}
+//
+// func (T[P]) m() {}
+// func (T[P]) n[Q any]() {}
+//
+// then, using S as go.shape.int:
+// - in T[int].m, obj is T[S] and sym.Name is "m".
+// - in T[int].n[int], obj is T[S].n[S] and sym.Name is "n".
+//
+// Note that we could have pushed dictionaries down to methods in every case,
+// but since non-generic methods will always share the same "type environment"
+// as their defining type, we can optimize by reusing the type's dictionary.
+func shapedMethodExpr(pos src.XPos, obj *ir.Name, sym *types.Sym) ir.Node {
+ if obj.Op() == ir.OTYPE {
+ // non-generic method on generic type
+ typ := obj.Type()
+ assert(typ.HasShape())
- method := func() *types.Field {
- for _, method := range typ.Methods() {
- if method.Sym == sym {
- return method
+ method := func() *types.Field {
+ for _, m := range typ.Methods() {
+ if m.Sym == sym {
+ return m
+ }
}
- }
- base.FatalfAt(pos, "failed to find method %v in shaped type %v", sym, typ)
- panic("unreachable")
- }()
+ base.FatalfAt(pos, "failed to find method %v in shaped type %v", sym, typ)
+ panic("unreachable")
+ }()
+
+ return typecheck.NewMethodExpr(pos, method.Type.Recv().Type, sym)
+ } else {
+ // generic method on possibly generic type
+ assert(obj.Op() == ir.ONAME && obj.Class == ir.PFUNC)
+ typ := obj.Type()
+ assert(typ.HasShape())
+
+ // OMETHEXPR assumes that the linker symbol to call looks like "<type sym>.<method sym>".
+ // This works because non-generic method symbols are relative to their type. But generic
+ // methods use fully-qualified names, so this won't work.
+ //
+ // To use OMETHEXPR for generic methods, we craft a dummy field on the type by removing
+ // the qualifier; OMETHEXPR will put it back later.
+ lsym := obj.Linksym().Name
+ // Since the method is generic, we know the method name must be followed by a bracket.
+ // TODO(mark): It's not ideal to rely on string naming here. Find a more robust solution.
+ msym := sym.Pkg.Lookup(lsym[strings.LastIndex(lsym, sym.Name+"["):])
+
+ // Note that the field name here includes the type arguments; while also not ideal, the
+ // types package does not seem to complain.
+ m := types.NewField(obj.Pos(), msym, typ)
+ m.Nname = obj
+
+ n := ir.NewSelectorExpr(pos, ir.OMETHEXPR, ir.TypeNode(typ.Recv().Type), msym)
+ n.Selection = m
+ n.SetType(typecheck.NewMethodType(typ, typ.Recv().Type))
+ n.SetTypecheck(1)
- // Construct an OMETHEXPR node.
- recv := method.Type.Recv().Type
- return typecheck.NewMethodExpr(pos, recv, sym)
+ return n
+ }
}
func (r *reader) multiExpr() []ir.Node {