diff options
| author | Mark Freeman <mark@golang.org> | 2026-03-30 13:09:06 -0400 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2026-03-31 15:05:08 -0700 |
| commit | cafe443251e5c0bd95849a0bd18c014530f3118c (patch) | |
| tree | 4752b8421f30e93ff3be9d32a354c5cdad7e9fbc /src/cmd/compile/internal/noder | |
| parent | fdd485c5f1701dfadb3a74d95c606329ec6463aa (diff) | |
| download | go-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.go | 90 |
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 { |
