Shared: Generalize typeConstraintBaseTypeMatch

This commit is contained in:
Tom Hvitved
2026-06-25 12:07:31 +02:00
parent 299c8cd914
commit bfc37e547f
3 changed files with 66 additions and 74 deletions

View File

@@ -469,7 +469,7 @@ mod method_non_parametric_trait_impl {
let i = thing.convert_to(); // $ type=i:S1 target=T::convert_to
let j = convert_to(thing); // $ target=convert_to $ MISSING: type=j:S1 -- the blanket implementation `impl<T: MyTrait<S1>> ConvertTo<S1> for T` is currently not included in the constraint analysis
let x = call_trait_m1_trait2_m3(MyThing { a: S2 }); // $ target=call_trait_m1_trait2_m3 $ MISSING: type=x:S1
let x = call_trait_m1_trait2_m3(MyThing { a: S2 }); // $ target=call_trait_m1_trait2_m3 type=x:S1
}
}

View File

@@ -8371,6 +8371,8 @@ inferType
| main.rs:469:17:469:34 | thing.convert_to() | | main.rs:249:5:250:14 | S1 |
| main.rs:470:28:470:32 | thing | | main.rs:238:5:241:5 | MyThing |
| main.rs:470:28:470:32 | thing | A | main.rs:249:5:250:14 | S1 |
| main.rs:472:13:472:13 | x | | main.rs:249:5:250:14 | S1 |
| main.rs:472:17:472:58 | call_trait_m1_trait2_m3(...) | | main.rs:249:5:250:14 | S1 |
| main.rs:472:41:472:57 | MyThing {...} | | main.rs:238:5:241:5 | MyThing |
| main.rs:472:41:472:57 | MyThing {...} | A | main.rs:251:5:252:14 | S2 |
| main.rs:472:54:472:55 | S2 | | main.rs:251:5:252:14 | S2 |

View File

@@ -1346,6 +1346,14 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
module MatchingWithEnvironment<MatchingWithEnvironmentInputSig Input> {
private import Input
pragma[nomagic]
private TypeParameter getDeclTypeParameter(Declaration decl, TypeArgumentPosition tapos) {
exists(TypeParameterPosition tppos |
result = decl.getTypeParameter(tppos) and
typeArgumentParameterPositionMatch(tapos, tppos)
)
}
/**
* Gets the type of the type argument at `path` in `a` that corresponds to
* the type parameter `tp` in `target`, if any.
@@ -1356,11 +1364,11 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
*/
bindingset[a, target]
pragma[inline_late]
private Type getTypeArgument(Access a, Declaration target, TypeParameter tp, TypePath path) {
exists(TypeArgumentPosition tapos, TypeParameterPosition tppos |
Type getTypeArgument(Access a, Declaration target, TypeParameter tp, TypePath path) {
exists(TypeArgumentPosition tapos |
result = a.getTypeArgument(tapos, path) and
tp = target.getTypeParameter(tppos) and
typeArgumentParameterPositionMatch(tapos, tppos)
tp = getDeclTypeParameter(target, tapos) and
not isPseudoType(result)
)
}
@@ -1526,42 +1534,35 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
private module AccessConstraint {
private predicate relevantAccessConstraint(
Access a, AccessEnvironment e, Declaration target, AccessPosition apos, TypePath path,
Access a, AccessEnvironment e, Declaration target, TypeParameter constrainedTp,
TypeMention constraint
) {
target = a.getTarget(e) and
typeParameterHasConstraint(target, apos, _, path, constraint)
typeParameterHasConstraint(target, constrainedTp, constraint)
}
private newtype TRelevantAccess =
MkRelevantAccess(Access a, AccessPosition apos, AccessEnvironment e, TypePath path) {
relevantAccessConstraint(a, e, _, apos, path, _)
MkRelevantAccess(Access a, AccessEnvironment e, TypeParameter constrainedTp) {
relevantAccessConstraint(a, e, _, constrainedTp, _)
}
/**
* If the access `a` for `apos`, environment `e`, and `path` has an inferred type
* which type inference requires to satisfy some constraint.
*/
private class RelevantAccess extends MkRelevantAccess {
Access a;
AccessPosition apos;
AccessEnvironment e;
TypePath path;
TypeParameter constrainedTp;
RelevantAccess() { this = MkRelevantAccess(a, apos, e, path) }
RelevantAccess() { this = MkRelevantAccess(a, e, constrainedTp) }
pragma[nomagic]
Type getTypeAt(TypePath suffix) {
result = a.getInferredType(e, apos, path.appendInverse(suffix))
}
Type getTypeAt(TypePath path) { typeMatch(a, e, _, path, result, constrainedTp) }
/** Gets the constraint that this relevant access should satisfy. */
TypeMention getConstraint(Declaration target) {
relevantAccessConstraint(a, e, target, apos, path, result)
relevantAccessConstraint(a, e, target, constrainedTp, result)
}
string toString() {
result = a.toString() + ", " + apos.toString() + ", " + path.toString()
result = a.toString() + ", " + e.toString() + ", " + constrainedTp.toString()
}
Location getLocation() { result = a.getLocation() }
@@ -1577,7 +1578,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
class TypeMatchingContext = Access;
TypeMatchingContext getTypeMatchingContext(RelevantAccess at) {
at = MkRelevantAccess(result, _, _, _)
at = MkRelevantAccess(result, _, _)
}
pragma[nomagic]
@@ -1591,41 +1592,32 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
SatisfiesTypeParameterConstraintInput>;
pragma[nomagic]
predicate satisfiesConstraintAtTypeParameter(
predicate argSatisfiesConstraintAtTypeParameter(
Access a, AccessEnvironment e, Declaration target, AccessPosition apos, TypePath prefix,
TypeMention constraint, TypePath pathToTypeParamInConstraint,
TypePath pathToTypeParamInSub
) {
exists(RelevantAccess ra |
ra = MkRelevantAccess(a, apos, e, prefix) and
exists(RelevantAccess ra, TypeParameter constrainedTp |
ra = MkRelevantAccess(a, e, constrainedTp) and
relevantAccessConstraint(a, e, target, constrainedTp, constraint) and
SatisfiesTypeParameterConstraint::satisfiesConstraintAtTypeParameter(ra, constraint,
pathToTypeParamInConstraint, pathToTypeParamInSub) and
constraint = ra.getConstraint(target)
exists(DeclarationPosition dpos |
accessDeclarationPositionMatch(apos, dpos) and
constrainedTp = target.getDeclaredType(dpos, prefix)
)
)
}
pragma[nomagic]
predicate satisfiesConstraint(
Access a, AccessEnvironment e, Declaration target, AccessPosition apos, TypePath prefix,
Access a, AccessEnvironment e, Declaration target, TypeParameter constrainedTp,
TypeMention constraint, TypePath path, Type t
) {
exists(RelevantAccess ra |
ra = MkRelevantAccess(a, apos, e, prefix) and
SatisfiesTypeParameterConstraint::satisfiesConstraint(ra, constraint, path, t) and
constraint = ra.getConstraint(target)
)
}
pragma[nomagic]
predicate satisfiesConstraintThrough(
Access a, AccessEnvironment e, Declaration target, AccessPosition apos, TypePath prefix,
TypeAbstraction abs, TypeMention constraint, TypePath path, Type t
) {
exists(RelevantAccess ra |
ra = MkRelevantAccess(a, apos, e, prefix) and
SatisfiesTypeParameterConstraint::satisfiesConstraintThrough(ra, abs, constraint, path,
t) and
constraint = ra.getConstraint(target)
ra = MkRelevantAccess(a, e, constrainedTp) and
relevantAccessConstraint(a, e, target, constrainedTp, constraint) and
SatisfiesTypeParameterConstraint::satisfiesConstraint(ra, constraint, path, t)
)
}
}
@@ -1644,51 +1636,44 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
}
/**
* Holds if the type parameter `constrainedTp` occurs in the declared type of
* `target` at `apos` and `pathToConstrained`, and there is a constraint
* `constraint` on `constrainedTp`.
* Holds if the type parameter `constrainedTp` applies to `target` and the
* constraint `constraint` applies to `constrainedTp`.
*/
pragma[nomagic]
private predicate typeParameterHasConstraint(
Declaration target, AccessPosition apos, TypeParameter constrainedTp,
TypePath pathToConstrained, TypeMention constraint
Declaration target, TypeParameter constrainedTp, TypeMention constraint
) {
exists(DeclarationPosition dpos |
accessDeclarationPositionMatch(apos, dpos) and
constrainedTp = target.getTypeParameter(_) and
constrainedTp = target.getDeclaredType(dpos, pathToConstrained) and
constraint = getATypeParameterConstraint(constrainedTp, target)
)
constrainedTp = target.getTypeParameter(_) and
constraint = getATypeParameterConstraint(constrainedTp, target)
}
/**
* Holds if the declared type of `target` contains a type parameter at
* `apos` and `pathToConstrained` that must satisfy `constraint` and `tp`
* occurs at `pathToTp` in `constraint`.
* Holds if the type parameter `constrainedTp` applies to `target`, the
* constraint `constraint` applies to `constrainedTp`, and type parameter
* `tp` occurs at `pathToTp` in `constraint`.
*
* For example, in
*
* ```csharp
* interface IFoo<A> { }
* T1 M<T1, T2>(T2 item) where T2 : IFoo<T1> { }
* ```
* with the method declaration being the target and with `apos`
* corresponding to `item`, we have the following
* - `pathToConstrained = ""`,
* - `tp = T1`,
*
* with the method declaration being the target, we have the following
* - `constrainedTp = T2`,
* - `constraint = IFoo`,
* - `tp = T1`, and
* - `pathToTp = "A"`.
*/
pragma[nomagic]
private predicate typeParameterConstraintHasTypeParameter(
Declaration target, AccessPosition apos, TypePath pathToConstrained, TypeMention constraint,
TypePath pathToTp, TypeParameter tp
Declaration target, TypeParameter constrainedTp, TypeMention constraint, TypePath pathToTp,
TypeParameter tp
) {
exists(TypeParameter constrainedTp |
typeParameterHasConstraint(target, apos, constrainedTp, pathToConstrained, constraint) and
tp = target.getTypeParameter(_) and
tp = constraint.getTypeAt(pathToTp) and
constrainedTp != tp
)
typeParameterHasConstraint(target, constrainedTp, constraint) and
tp = target.getTypeParameter(_) and
tp = constraint.getTypeAt(pathToTp) and
constrainedTp != tp
}
pragma[nomagic]
@@ -1696,9 +1681,9 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
Access a, AccessEnvironment e, Declaration target, TypePath path, Type t, TypeParameter tp
) {
not exists(getTypeArgument(a, target, tp, _)) and
exists(TypeMention constraint, AccessPosition apos, TypePath pathToTp, TypePath pathToTp2 |
typeParameterConstraintHasTypeParameter(target, apos, pathToTp2, constraint, pathToTp, tp) and
AccessConstraint::satisfiesConstraint(a, e, target, apos, pathToTp2, constraint,
exists(TypeMention constraint, TypeParameter constrainedTp, TypePath pathToTp |
typeParameterConstraintHasTypeParameter(target, constrainedTp, constraint, pathToTp, tp) and
AccessConstraint::satisfiesConstraint(a, e, target, constrainedTp, constraint,
pathToTp.appendInverse(path), t)
)
}
@@ -1781,7 +1766,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
Declaration target, TypePath prefix, TypeMention constraint,
TypePath pathToTypeParamInConstraint, TypePath pathToTypeParamInSub
|
AccessConstraint::satisfiesConstraintAtTypeParameter(a, e, target, apos, prefix,
AccessConstraint::argSatisfiesConstraintAtTypeParameter(a, e, target, apos, prefix,
constraint, pathToTypeParamInConstraint, pathToTypeParamInSub)
|
exists(TypePath suffix |
@@ -1847,7 +1832,12 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
*/
typeMatch(a, e, target, suffix, result, tp) and
typeParameterConstraintHasTypeParameter(target, apos, _, constraint, pathToTp, tp) and
exists(TypeParameter constrainedTp, DeclarationPosition dpos |
typeParameterConstraintHasTypeParameter(target, constrainedTp, constraint, pathToTp,
tp) and
accessDeclarationPositionMatch(apos, dpos) and
constrainedTp = target.getDeclaredType(dpos, _)
) and
pathToTp = pathToTypeParamInConstraint.appendInverse(mid) and
path = prefix.append(pathToTypeParamInSub.append(mid).append(suffix))
)