Shared: Infer types for type parameters with contraints

This commit is contained in:
Simon Friis Vindum
2025-03-25 09:03:44 +01:00
parent 831413b5ec
commit 06c8963f70
3 changed files with 85 additions and 26 deletions

View File

@@ -307,8 +307,8 @@ mod function_trait_bounds {
let x2 = MyThing { a: S1 };
let y2 = MyThing { a: S2 };
println!("{:?}", call_trait_m1(x2)); // missing
println!("{:?}", call_trait_m1(y2)); // missing
println!("{:?}", call_trait_m1(x2));
println!("{:?}", call_trait_m1(y2));
let x3 = MyThing {
a: MyThing { a: S1 },
@@ -317,8 +317,8 @@ mod function_trait_bounds {
a: MyThing { a: S2 },
};
println!("{:?}", call_trait_thing_m1(x3)); // missing
println!("{:?}", call_trait_thing_m1(y3)); // missing
println!("{:?}", call_trait_thing_m1(x3));
println!("{:?}", call_trait_thing_m1(y3));
}
}

View File

@@ -409,8 +409,10 @@ inferType
| main.rs:308:18:308:34 | MyThing {...} | | main.rs:257:5:260:5 | struct MyThing |
| main.rs:308:18:308:34 | MyThing {...} | T | main.rs:264:5:265:14 | struct S2 |
| main.rs:308:31:308:32 | S2 | | main.rs:264:5:265:14 | struct S2 |
| main.rs:310:26:310:42 | call_trait_m1(...) | | main.rs:262:5:263:14 | struct S1 |
| main.rs:310:40:310:41 | x2 | | main.rs:257:5:260:5 | struct MyThing |
| main.rs:310:40:310:41 | x2 | T | main.rs:262:5:263:14 | struct S1 |
| main.rs:311:26:311:42 | call_trait_m1(...) | | main.rs:264:5:265:14 | struct S2 |
| main.rs:311:40:311:41 | y2 | | main.rs:257:5:260:5 | struct MyThing |
| main.rs:311:40:311:41 | y2 | T | main.rs:264:5:265:14 | struct S2 |
| main.rs:313:13:313:14 | x3 | | main.rs:257:5:260:5 | struct MyThing |
@@ -431,9 +433,11 @@ inferType
| main.rs:317:16:317:32 | MyThing {...} | | main.rs:257:5:260:5 | struct MyThing |
| main.rs:317:16:317:32 | MyThing {...} | T | main.rs:264:5:265:14 | struct S2 |
| main.rs:317:29:317:30 | S2 | | main.rs:264:5:265:14 | struct S2 |
| main.rs:320:26:320:48 | call_trait_thing_m1(...) | | main.rs:262:5:263:14 | struct S1 |
| main.rs:320:46:320:47 | x3 | | main.rs:257:5:260:5 | struct MyThing |
| main.rs:320:46:320:47 | x3 | T | main.rs:257:5:260:5 | struct MyThing |
| main.rs:320:46:320:47 | x3 | T.T | main.rs:262:5:263:14 | struct S1 |
| main.rs:321:26:321:48 | call_trait_thing_m1(...) | | main.rs:264:5:265:14 | struct S2 |
| main.rs:321:46:321:47 | y3 | | main.rs:257:5:260:5 | struct MyThing |
| main.rs:321:46:321:47 | y3 | T | main.rs:257:5:260:5 | struct MyThing |
| main.rs:321:46:321:47 | y3 | T.T | main.rs:264:5:265:14 | struct S2 |

View File

@@ -512,36 +512,35 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
private module AccessBaseType {
/**
* Holds if inferring types at `a` might depend on the type at `apos`
* having `baseMention` as a transitive base type mention.
* Holds if inferring types at `a` might depend on the type at `path` of
* `apos` having `baseMention` as a transitive base type mention.
*/
private predicate relevantAccess(Access a, AccessPosition apos, Type base) {
private predicate relevantAccess(Access a, AccessPosition apos, TypePath path, Type base) {
exists(Declaration target, DeclarationPosition dpos |
adjustedAccessType(a, apos, target, _, _) and
accessDeclarationPositionMatch(apos, dpos) and
declarationBaseType(target, dpos, base, _, _)
accessDeclarationPositionMatch(apos, dpos)
|
path.isEmpty() and declarationBaseType(target, dpos, base, _, _)
or
typeParameterConstraintHasTypeParameter(target, dpos, path, _, base, _, _)
)
}
pragma[nomagic]
private Type inferRootType(Access a, AccessPosition apos) {
relevantAccess(a, apos, _) and
result = a.getInferredType(apos, TypePath::nil())
}
pragma[nomagic]
private Type inferTypeAt(Access a, AccessPosition apos, TypeParameter tp, TypePath suffix) {
relevantAccess(a, apos, _) and
private Type inferTypeAt(
Access a, AccessPosition apos, TypePath prefix, TypeParameter tp, TypePath suffix
) {
relevantAccess(a, apos, prefix, _) and
exists(TypePath path0 |
result = a.getInferredType(apos, path0) and
result = a.getInferredType(apos, prefix.append(path0)) and
path0.isCons(tp, suffix)
)
}
/**
* Holds if `baseMention` is a (transitive) base type mention of the type of
* `a` at position `apos`, and `t` is mentioned (implicitly) at `path` inside
* `base`. For example, in
* Holds if `baseMention` is a (transitive) base type mention of the
* type of `a` at position `apos` at path `pathToSub`, and `t` is
* mentioned (implicitly) at `path` inside `base`. For example, in
*
* ```csharp
* class C<T1> { }
@@ -570,17 +569,18 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
*/
pragma[nomagic]
predicate hasBaseTypeMention(
Access a, AccessPosition apos, TypeMention baseMention, TypePath path, Type t
Access a, AccessPosition apos, TypePath pathToSub, TypeMention baseMention, TypePath path,
Type t
) {
relevantAccess(a, apos, resolveTypeMentionRoot(baseMention)) and
exists(Type sub | sub = inferRootType(a, apos) |
relevantAccess(a, apos, pathToSub, resolveTypeMentionRoot(baseMention)) and
exists(Type sub | sub = a.getInferredType(apos, pathToSub) |
not t = sub.getATypeParameter() and
baseTypeMentionHasTypeAt(sub, baseMention, path, t)
or
exists(TypePath prefix, TypePath suffix, TypeParameter tp |
tp = sub.getATypeParameter() and
baseTypeMentionHasTypeAt(sub, baseMention, prefix, tp) and
t = inferTypeAt(a, apos, tp, suffix) and
t = inferTypeAt(a, apos, pathToSub, tp, suffix) and
path = prefix.append(suffix)
)
)
@@ -596,7 +596,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
Access a, AccessPosition apos, Type base, TypePath path, Type t
) {
exists(TypeMention tm |
AccessBaseType::hasBaseTypeMention(a, apos, tm, path, t) and
AccessBaseType::hasBaseTypeMention(a, apos, TypePath::nil(), tm, path, t) and
base = resolveTypeMentionRoot(tm)
)
}
@@ -671,6 +671,58 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
t = getTypeArgument(a, target, tp, path)
}
/**
* Holds if `tp1` and `tp2` are distinct type parameters of `target`, the
* declared type at `apos` mentions `tp1` at `path1`, `tp1` has a base
* type mention of type `constrant` that mentions `tp2` at the path
* `path2`.
*
* For this example
* ```csharp
* interface IFoo<A> { }
* void M<T1, T2>(T2 item) where T2 : IFoo<T1> { }
* ```
* with the method declaration being the target and the for the first
* parameter position, we have the following
* - `path1 = ""`,
* - `tp1 = T2`,
* - `constraint = IFoo`,
* - `path2 = "A"`,
* - `tp2 = T1`
*/
pragma[nomagic]
private predicate typeParameterConstraintHasTypeParameter(
Declaration target, DeclarationPosition dpos, TypePath path1, TypeParameter tp1,
Type constraint, TypePath path2, TypeParameter tp2
) {
tp1 = target.getTypeParameter(_) and
tp2 = target.getTypeParameter(_) and
tp1 != tp2 and
tp1 = target.getDeclaredType(dpos, path1) and
exists(TypeMention tm |
tm = getABaseTypeMention(tp1) and
tm.resolveTypeAt(path2) = tp2 and
constraint = resolveTypeMentionRoot(tm)
)
}
pragma[nomagic]
private predicate typeConstraintBaseTypeMatch(
Access a, Declaration target, TypePath path, Type t, TypeParameter tp
) {
not exists(getTypeArgument(a, target, tp, _)) and
target = a.getTarget() and
exists(
TypeMention base, AccessPosition apos, DeclarationPosition dpos, TypePath pathToTp,
TypePath pathToTp2
|
accessDeclarationPositionMatch(apos, dpos) and
typeParameterConstraintHasTypeParameter(target, dpos, pathToTp2, _,
resolveTypeMentionRoot(base), pathToTp, tp) and
AccessBaseType::hasBaseTypeMention(a, apos, pathToTp2, base, pathToTp.append(path), t)
)
}
pragma[inline]
private predicate typeMatch(
Access a, Declaration target, TypePath path, Type t, TypeParameter tp
@@ -684,6 +736,9 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
or
// We can infer the type of `tp` by going up the type hiearchy
baseTypeMatch(a, target, path, t, tp)
or
// We can infer the type of `tp` by a type bound
typeConstraintBaseTypeMatch(a, target, path, t, tp)
}
/**