@@ -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+ populateTypeParamOrigin (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,48 @@ func checkObjectNotSpecialized(obj types.Object) {
21212102 }
21222103 }
21232104}
2105+
2106+ // getTypeParamOrigin return the origin type param for a type param from
2107+ // 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+ // populateTypeParamOrigin records for each type param of a method the type
2116+ // param as it occurs in the source code. This allows us to record a unique
2117+ // constraint as part of `typeparams`.
2118+ //
2119+ // Consider a generic struct and a generic method:
2120+ //
2121+ // type S[P any] struct{}
2122+ // func (*S[P]) m[Q ~P](x Q) {}
2123+ //
2124+ // If we have a variable 's' of type 'S[int]' and the expression 's.m[int](42)',
2125+ // then the type of the selector expression 's.m' is 'func[Q ~int](Q)'. The
2126+ // method 'm' here is an instantiation of the declaration, which has its own
2127+ // type with type parameter 'Q' with constraint 'interface { ~int }'. As we
2128+ // so not extract method instantiations, but only their origins, we want to
2129+ // match this behavior for the constraints of instantiations. To achieve this
2130+ // function records a mapping between the type parameters from a method
2131+ // instantiation and the type parameters from its origin.
2132+ func populateTypeParamOrigin (tp * types.Named , i int , meth * types.Func ) {
2133+ if tp .Method (i ) == meth {
2134+ return
2135+ }
2136+
2137+ instantiatedParams := tp .Method (i ).Type ().(* types.Signature ).TypeParams ()
2138+ originParams := meth .Type ().(* types.Signature ).TypeParams ()
2139+
2140+ if instantiatedParams .Len () != originParams .Len () {
2141+ log .Fatalf ("Method instantiation %s has %d type parameters, origin has %d" , tp .Method (i ), instantiatedParams .Len (), originParams .Len ())
2142+ }
2143+
2144+ for j := 0 ; j < instantiatedParams .Len (); j ++ {
2145+ instantiatedParam := instantiatedParams .At (j )
2146+ setTypeParamParent (instantiatedParam , meth , false )
2147+ typeParamOrigin [instantiatedParam ] = originParams .At (j )
2148+ }
2149+ }
0 commit comments