Skip to content

Commit 2178247

Browse files
committed
Go: Only output origin constraints for methods
1 parent 17afaf2 commit 2178247

2 files changed

Lines changed: 56 additions & 26 deletions

File tree

go/extractor/extractor.go

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ type typeParamParentEntry struct {
4040

4141
var 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+
4345
func 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+
}

go/ql/test/library-tests/semmle/go/Function/TypeParamType.expected

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@ numberOfTypeParameters
5555
| codeql-go-tests/function.StructForGenericMethod2.GenericMethod2 | 0 | from receiver | P3 | interface { } |
5656
| codeql-go-tests/function.StructWithDependentBound | 0 | | P5 | interface { } |
5757
| codeql-go-tests/function.StructWithDependentBound.GenericMethodWithDependentBound | 0 | | P7 | interface { ~[]P6 } |
58-
| codeql-go-tests/function.StructWithDependentBound.GenericMethodWithDependentBound | 0 | | P7 | interface { ~[]int } |
59-
| codeql-go-tests/function.StructWithDependentBound.GenericMethodWithDependentBound | 0 | | P7 | interface { ~[]string } |
6058
| codeql-go-tests/function.StructWithDependentBound.GenericMethodWithDependentBound | 0 | from receiver | P6 | interface { } |
6159
| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 0 | | _ | interface { } |
6260
| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 1 | | _ | interface { string } |

0 commit comments

Comments
 (0)