@@ -38,8 +38,16 @@ type typeParamParentEntry struct {
3838 isFromReceiver bool
3939}
4040
41+ // typeParamMutex protects typeParamParent.
42+ var typeParamParentMutex sync.RWMutex
43+
4144var typeParamParent map [* types.TypeParam ]typeParamParentEntry = make (map [* types.TypeParam ]typeParamParentEntry )
4245
46+ // typeParamMutex protects typeParamOrigin.
47+ var typeParamOriginMutex sync.RWMutex
48+
49+ var typeParamOrigin map [* types.TypeParam ]* types.TypeParam = make (map [* types.TypeParam ]* types.TypeParam )
50+
4351func init () {
4452 // this sets the number of threads that the Go runtime will spawn; this is separate
4553 // from the number of goroutines that the program spawns, which are scheduled into
@@ -1658,29 +1666,7 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label {
16581666 for i := 0 ; i < origintp .NumMethods (); i ++ {
16591667 meth := origintp .Method (i ).Origin ()
16601668 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- }
1669+ populateTypeParamParentsAndOrigins (tp .Method (i ), meth )
16841670 }
16851671
16861672 underlyingInterface , underlyingIsInterface := underlying .(* types.Interface )
@@ -1704,7 +1690,8 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label {
17041690 case * types.TypeParam :
17051691 kind = dbscheme .TypeParamType .Index ()
17061692 parentlbl , isReceiverChild := getTypeParamParentLabel (tw , tp )
1707- constraintLabel := extractType (tw , tp .Constraint ())
1693+ constraint := getTypeParamOrigin (tp ).Constraint ()
1694+ constraintLabel := extractType (tw , constraint )
17081695 dbscheme .TypeParamTable .Emit (tw , lbl , tp .Obj ().Name (), constraintLabel , parentlbl , tp .Index (), isReceiverChild )
17091696 case * types.Union :
17101697 kind = dbscheme .TypeSetLiteral .Index ()
@@ -2062,7 +2049,10 @@ func getObjectBeingUsed(tw *trap.Writer, ident *ast.Ident) types.Object {
20622049}
20632050
20642051func getTypeParamParentLabel (tw * trap.Writer , tp * types.TypeParam ) (trap.Label , bool ) {
2052+ typeParamParentMutex .RLock ()
20652053 entry , exists := typeParamParent [tp ]
2054+ typeParamParentMutex .RUnlock ()
2055+
20662056 if ! exists {
20672057 log .Fatalf ("Parent of type parameter does not exist: %s %s" , tp .String (), tp .Constraint ().String ())
20682058 }
@@ -2073,7 +2063,11 @@ func getTypeParamParentLabel(tw *trap.Writer, tp *types.TypeParam) (trap.Label,
20732063 return parentlbl , entry .isFromReceiver
20742064}
20752065
2066+ // setTypeParamParentLocked requires typeParamMutex to be held for writing.
20762067func setTypeParamParent (tp * types.TypeParam , parent types.Object , isFromReceiver bool ) {
2068+ typeParamParentMutex .Lock ()
2069+ defer typeParamParentMutex .Unlock ()
2070+
20772071 entry , exists := typeParamParent [tp ]
20782072 newEntry := typeParamParentEntry {parent , isFromReceiver }
20792073 if ! exists {
@@ -2121,3 +2115,76 @@ func checkObjectNotSpecialized(obj types.Object) {
21212115 }
21222116 }
21232117}
2118+
2119+ // getTypeParamOrigin returns the origin type parameter for a type parameter
2120+ // from an instantiated method.
2121+ func getTypeParamOrigin (tp * types.TypeParam ) * types.TypeParam {
2122+ typeParamOriginMutex .RLock ()
2123+ origin , exists := typeParamOrigin [tp ]
2124+ typeParamOriginMutex .RUnlock ()
2125+
2126+ if exists {
2127+ return origin
2128+ } else {
2129+ return tp
2130+ }
2131+ }
2132+
2133+ // populateTypeParamParentsAndOrigins records for each type parameter of a method
2134+ // the origin parent and type parameter.
2135+ //
2136+ // Consider a generic struct and a generic method:
2137+ //
2138+ // type S[P any] struct{}
2139+ // func (*S[P]) m[Q ~P](x Q) {}
2140+ //
2141+ // If we have a variable 's' of type 'S[int]' and the expression 's.m[int](42)',
2142+ // then the type of the selector expression 's.m' is 'func[Q ~int](Q)'. The
2143+ // method 'm' here is an instantiation of the declaration, which has its own
2144+ // type with type parameter 'Q' with constraint 'interface { ~int }'. As we
2145+ // do not extract method instantiations, but only their origins, we want to
2146+ // match this behavior for the constraints of instantiated type parameter, and
2147+ // record their origin. Moreover, not extracting instantiations also means that
2148+ // 'populateTypeParamParents' does not automatically get called on their type
2149+ // parameters. To compensate, we add the type params here by calling
2150+ // `setTypeParamParent`.
2151+ //
2152+ // As the parent of a type parameter use the origin method. This suffices, as
2153+ // the name and index of the type parameter in the instantiation will be
2154+ // identical to those of the uninstantiated method, and as only a constraint and
2155+ // these two properties will be extracted for a type parameter.
2156+ func populateTypeParamParentsAndOrigins (meth * types.Func , originmeth * types.Func ) {
2157+ if meth == originmeth {
2158+ return
2159+ }
2160+
2161+ typeparams := meth .Type ().(* types.Signature ).TypeParams ()
2162+ populateTypeParamParents (typeparams , originmeth , false )
2163+
2164+ origintypeparams := originmeth .Type ().(* types.Signature ).TypeParams ()
2165+ populateTypeParamOrigins (meth , typeparams , origintypeparams )
2166+ }
2167+
2168+ func populateTypeParamOrigins (meth * types.Func , typeparams * types.TypeParamList , origintypeparams * types.TypeParamList ) {
2169+ if typeparams .Len () != origintypeparams .Len () {
2170+ log .Fatalf ("Method instantiation %s has %d type parameters, origin has %d" ,
2171+ meth , typeparams .Len (), origintypeparams .Len ())
2172+ }
2173+
2174+ for j := 0 ; j < typeparams .Len (); j ++ {
2175+ setTypeParamOrigin (typeparams .At (j ), origintypeparams .At (j ))
2176+ }
2177+ }
2178+
2179+ func setTypeParamOrigin (typeparam * types.TypeParam , origintypeparam * types.TypeParam ) {
2180+ typeParamOriginMutex .Lock ()
2181+ defer typeParamOriginMutex .Unlock ()
2182+
2183+ entry , exists := typeParamOrigin [typeparam ]
2184+ if ! exists {
2185+ typeParamOrigin [typeparam ] = origintypeparam
2186+ } else if entry != origintypeparam {
2187+ log .Fatalf ("Origin of type parameter '%s %s' being set to a different value: '%s' vs '%s'" ,
2188+ typeparam .String (), typeparam .Constraint ().String (), entry .String (), origintypeparam .String ())
2189+ }
2190+ }
0 commit comments