Skip to content

compiler: exclude generic methods from runtime method sets#5544

Merged
deadprogram merged 3 commits into
tinygo-org:devfrom
dgryski:dgryski/generic-methods
Jul 23, 2026
Merged

compiler: exclude generic methods from runtime method sets#5544
deadprogram merged 3 commits into
tinygo-org:devfrom
dgryski:dgryski/generic-methods

Conversation

@dgryski

@dgryski dgryski commented Jul 22, 2026

Copy link
Copy Markdown
Member

Go 1.27 promoted "generic methods" (methods with their own type parameters, independent of any type parameters on the receiver) out of the GenericMethods experiment, e.g.:

func (r *Rand) N[Int intType](n Int) Int

Boxing a value of a type with such a method into an interface caused tinygo to panic while building the runtime type's method table:

getTypeCode -> getMethodSetValue -> getTypeCodeName

getTypeCodeName's type switch has no case for *types.TypeParam, and a generic method's Signature carries the method's own type parameters in its parameter/result types (e.g. "Int" above), so encoding it into a type code name panicked with "unknown type: ".

This affected any package using math/rand/v2.Rand.N, including much of the math/rand/v2 test suite, since printing or otherwise boxing a *Rand into an interface is common.

Upstream reflect deliberately excludes generic methods from Type.NumMethod()/Type.Method() (they can't be instantiated implicitly, and a generic method can never satisfy an interface method), so mirror that behavior: add isGenericMethod, which checks whether a method's Signature has its own type parameters, and skip such methods when building the runtime method count, method set value, and method set in compiler/interface.go.

Fixes a panic isolated to:

package main

type T struct{}

func (t T) M[X int](n X) X { return n }

func main() {
	var t T
	var i interface{} = t
	_ = i
}

Go 1.27 promoted "generic methods" (methods with their own type
parameters, independent of any type parameters on the receiver) out
of the GenericMethods experiment, e.g.:

    func (r *Rand) N[Int intType](n Int) Int

Boxing a value of a type with such a method into an interface caused
tinygo to panic while building the runtime type's method table:

    getTypeCode -> getMethodSetValue -> getTypeCodeName

getTypeCodeName's type switch has no case for *types.TypeParam, and a
generic method's Signature carries the method's own type parameters
in its parameter/result types (e.g. "Int" above), so encoding it into
a type code name panicked with "unknown type: <param name>".

This affected any package using math/rand/v2.Rand.N, including much of
the math/rand/v2 test suite, since printing or otherwise boxing a
*Rand into an interface is common.

Upstream reflect deliberately excludes generic methods from
Type.NumMethod()/Type.Method() (they can't be instantiated implicitly,
and a generic method can never satisfy an interface method), so mirror
that behavior: add isGenericMethod, which checks whether a method's
Signature has its own type parameters, and skip such methods when
building the runtime method count, method set value, and method set
in compiler/interface.go.

Fixes a panic isolated to:

    package main

    type T struct{}

    func (t T) M[X int](n X) X { return n }

    func main() {
    	var t T
    	var i interface{} = t
    	_ = i
    }
Comment thread compiler/interface.go Outdated
dgryski added 2 commits July 22, 2026 13:19
hasMethodSet was computed from the raw, unfiltered method set length
(ms.Len() != 0), before generic methods were excluded from numMethods
and the method set value. For a type whose only method is generic
(e.g. "func (t T) M[X int](n X) X"), this left hasMethodSet true even
though the actual (filtered) method set is empty, causing an
unnecessary empty method-set global and methodSet field to be emitted
for that type's type descriptor.

Compute hasMethodSet from the same filtered loop that produces
numMethods, so it's true only when at least one non-generic method
exists.
Add compiler/testdata/go1.27.go, gated behind Go >= 1.27 (the version
that promoted generic methods out of the GenericMethods experiment),
covering both fixes from the previous two commits:

  - genericMethod has a regular method and a generic method (its own
    type parameter); boxing it into an interface must only include the
    regular method in the runtime method set instead of panicking in
    getTypeCodeName.
  - onlyGenericMethod's sole method is generic, so its type code must
    have hasMethodSet == false and no methodSet field at all, not an
    empty one.

Verified this test panics on the pre-fix compiler/interface.go and
passes after it.
@deadprogram

Copy link
Copy Markdown
Member

Thank you for working on this @dgryski and to @jakebailey for review. Now merging.

@deadprogram
deadprogram merged commit 2a49216 into tinygo-org:dev Jul 23, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants