@@ -40,6 +40,8 @@ type typeParamParentEntry struct {
4040
4141var typeParamParent map [* types.TypeParam ]typeParamParentEntry = make (map [* types.TypeParam ]typeParamParentEntry )
4242
43+ var typeParamOrigin map [* types.TypeParam ]* types.TypeParam = make (map [* types.TypeParam ]* types.TypeParam )
44+
4345func init () {
4446 // this sets the number of threads that the Go runtime will spawn; this is separate
4547 // from the number of goroutines that the program spawns, which are scheduled into
@@ -1658,29 +1660,8 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label {
16581660 for i := 0 ; i < origintp .NumMethods (); i ++ {
16591661 meth := origintp .Method (i ).Origin ()
16601662 extractMethod (tw , meth )
1663+ populateTypeParamOrigin (tp , i , meth )
16611664
1662- // Consider a generic struct and a generic method:
1663- //
1664- // type S[P any] struct{}
1665- // func (*S[P]) m[Q any](x Q) {}
1666- //
1667- // If we have a variable 's' of type 'S[int]' and the expression
1668- // 's.m[string]("")', then the type of the selector expression 's.m'
1669- // is ' func(Q)'. The method 'm' here is an instantiation of the
1670- // declaration, which has its own type with type parameter 'Q'.
1671- // As we do not extract method instantiations, 'populateTypeParamParents'
1672- // does not automatically get called for the type parameter 'Q'
1673- // from the instantiation of 'm'. To compensate, we add the type
1674- // parameters here.
1675- //
1676- // As a parent we use the origin method. This suffices, as the name
1677- // and index of the type parameter in the instantiation will be
1678- // identical to those of the uninstantiated method, and as only
1679- // these two properties will be extracted for a type parameter.
1680- if tp .Method (i ) != meth {
1681- signature := tp .Method (i ).Type ().(* types.Signature )
1682- populateTypeParamParents (signature .TypeParams (), meth , false )
1683- }
16841665 }
16851666
16861667 underlyingInterface , underlyingIsInterface := underlying .(* types.Interface )
@@ -1704,7 +1685,8 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label {
17041685 case * types.TypeParam :
17051686 kind = dbscheme .TypeParamType .Index ()
17061687 parentlbl , isReceiverChild := getTypeParamParentLabel (tw , tp )
1707- constraintLabel := extractType (tw , tp .Constraint ())
1688+ constraint := getTypeParamOrigin (tp ).Constraint ()
1689+ constraintLabel := extractType (tw , constraint )
17081690 dbscheme .TypeParamTable .Emit (tw , lbl , tp .Obj ().Name (), constraintLabel , parentlbl , tp .Index (), isReceiverChild )
17091691 case * types.Union :
17101692 kind = dbscheme .TypeSetLiteral .Index ()
@@ -2121,3 +2103,48 @@ func checkObjectNotSpecialized(obj types.Object) {
21212103 }
21222104 }
21232105}
2106+
2107+ // getTypeParamOrigin return the origin type param for a type param from
2108+ // an instantiated method.
2109+ func getTypeParamOrigin (tp * types.TypeParam ) * types.TypeParam {
2110+ if origin , exists := typeParamOrigin [tp ]; exists {
2111+ return origin
2112+ }
2113+ return tp
2114+ }
2115+
2116+ // populateTypeParamOrigin records for each type param of a method the type
2117+ // param as it occurs in the source code. This allows us to record a unique
2118+ // constraint as part of `typeparams`.
2119+ //
2120+ // Consider a generic struct and a generic method:
2121+ //
2122+ // type S[P any] struct{}
2123+ // func (*S[P]) m[Q ~P](x Q) {}
2124+ //
2125+ // If we have a variable 's' of type 'S[int]' and the expression 's.m[int](42)',
2126+ // then the type of the selector expression 's.m' is 'func[Q ~int](Q)'. The
2127+ // method 'm' here is an instantiation of the declaration, which has its own
2128+ // type with type parameter 'Q' with constraint 'interface { ~int }'. As we
2129+ // so not extract method instantiations, but only their origins, we want to
2130+ // match this behavior for the constraints of instantiations. To achieve this
2131+ // function records a mapping between the type parameters from a method
2132+ // instantiation and the type parameters from its origin.
2133+ func populateTypeParamOrigin (tp * types.Named , i int , meth * types.Func ) {
2134+ if tp .Method (i ) == meth {
2135+ return
2136+ }
2137+
2138+ instantiatedParams := tp .Method (i ).Type ().(* types.Signature ).TypeParams ()
2139+ originParams := meth .Type ().(* types.Signature ).TypeParams ()
2140+
2141+ if instantiatedParams .Len () != originParams .Len () {
2142+ log .Fatalf ("Method instantiation %s has %d type parameters, origin has %d" , tp .Method (i ), instantiatedParams .Len (), originParams .Len ())
2143+ }
2144+
2145+ for j := 0 ; j < instantiatedParams .Len (); j ++ {
2146+ instantiatedParam := instantiatedParams .At (j )
2147+ setTypeParamParent (instantiatedParam , meth , false )
2148+ typeParamOrigin [instantiatedParam ] = originParams .At (j )
2149+ }
2150+ }
0 commit comments