@@ -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,7 @@ 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 )
1661-
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- }
1663+ populateTypeParamParentAndOrigin (tp , i , meth )
16841664 }
16851665
16861666 underlyingInterface , underlyingIsInterface := underlying .(* types.Interface )
@@ -1704,7 +1684,8 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label {
17041684 case * types.TypeParam :
17051685 kind = dbscheme .TypeParamType .Index ()
17061686 parentlbl , isReceiverChild := getTypeParamParentLabel (tw , tp )
1707- constraintLabel := extractType (tw , tp .Constraint ())
1687+ constraint := getTypeParamOrigin (tp ).Constraint ()
1688+ constraintLabel := extractType (tw , constraint )
17081689 dbscheme .TypeParamTable .Emit (tw , lbl , tp .Obj ().Name (), constraintLabel , parentlbl , tp .Index (), isReceiverChild )
17091690 case * types.Union :
17101691 kind = dbscheme .TypeSetLiteral .Index ()
@@ -2121,3 +2102,54 @@ func checkObjectNotSpecialized(obj types.Object) {
21212102 }
21222103 }
21232104}
2105+
2106+ // getTypeParamOrigin return the origin type parameter of a type parameter
2107+ // from an instantiated method.
2108+ func getTypeParamOrigin (tp * types.TypeParam ) * types.TypeParam {
2109+ if origin , exists := typeParamOrigin [tp ]; exists {
2110+ return origin
2111+ }
2112+ return tp
2113+ }
2114+
2115+ // populateTypeParamParentAndOrigin records for each type parameter of a method
2116+ // the origin parent and type parameter.
2117+ //
2118+ // Consider a generic struct and a generic method:
2119+ //
2120+ // type S[P any] struct{}
2121+ // func (*S[P]) m[Q ~P](x Q) {}
2122+ //
2123+ // If we have a variable 's' of type 'S[int]' and the expression 's.m[int](42)',
2124+ // then the type of the selector expression 's.m' is 'func[Q ~int](Q)'. The
2125+ // method 'm' here is an instantiation of the declaration, which has its own
2126+ // type with type parameter 'Q' with constraint 'interface { ~int }'. As we
2127+ // do not extract method instantiations, but only their origins, we want to
2128+ // match this behavior for the constraints of instantiated type parameter, and
2129+ // record their origin. Moreover, not extracting instantiations also means that
2130+ // 'populateTypeParamParents' does not automatically get called on their type
2131+ // parameters. To compensate, we add the type params here by calling
2132+ // `setTypeParamParent`.
2133+ //
2134+ // As the parent of a type parameter use the origin method. This suffices, as
2135+ // the name and index of the type parameter in the instantiation will be
2136+ // identical to those of the uninstantiated method, and as only a constraint and
2137+ // these two properties will be extracted for a type parameter.
2138+ func populateTypeParamParentAndOrigin (tp * types.Named , i int , meth * types.Func ) {
2139+ if tp .Method (i ) == meth {
2140+ return
2141+ }
2142+
2143+ instantiatedParams := tp .Method (i ).Type ().(* types.Signature ).TypeParams ()
2144+ originParams := meth .Type ().(* types.Signature ).TypeParams ()
2145+
2146+ if instantiatedParams .Len () != originParams .Len () {
2147+ log .Fatalf ("Method instantiation %s has %d type parameters, origin has %d" , tp .Method (i ), instantiatedParams .Len (), originParams .Len ())
2148+ }
2149+
2150+ for j := 0 ; j < instantiatedParams .Len (); j ++ {
2151+ instantiatedParam := instantiatedParams .At (j )
2152+ setTypeParamParent (instantiatedParam , meth , false )
2153+ typeParamOrigin [instantiatedParam ] = originParams .At (j )
2154+ }
2155+ }
0 commit comments