Rust: Infer argument types based on trait bounds on parameters

This commit is contained in:
Tom Hvitved
2026-01-19 14:53:22 +01:00
parent b8a8a160c5
commit 6dc98cfd01
9 changed files with 653 additions and 242 deletions

View File

@@ -964,7 +964,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
/**
* Holds if `term` does not satisfy `constraint`.
*
* This predicate is an approximation of `not hasConstraintMention(term, constraint)`.
* This predicate is an approximation of `not hasConstraintMention(term, _, _, constraint, _, _)`.
*/
pragma[nomagic]
private predicate hasNotConstraintMention(
@@ -1072,7 +1072,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
}
pragma[nomagic]
private predicate satisfiesConstraintTypeMention1(
private predicate satisfiesConstraint0(
Term term, Constraint constraint, TypeAbstraction abs, TypeMention sub, TypePath path,
Type t
) {
@@ -1100,37 +1100,55 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
}
pragma[inline]
private predicate satisfiesConstraintTypeMentionInline(
Term term, Constraint constraint, TypeAbstraction abs, TypePath path,
TypePath pathToTypeParamInSub
private predicate satisfiesConstraintInline(
Term term, Constraint constraint, TypeAbstraction abs, TypePath pathToTypeParamInConstraint,
TypePath pathToTypeParamInSub, TypeParameter tp
) {
exists(TypeMention sub, TypeParameter tp |
satisfiesConstraintTypeMention1(term, constraint, abs, sub, path, tp) and
exists(TypeMention sub |
satisfiesConstraint0(term, constraint, abs, sub, pathToTypeParamInConstraint, tp) and
tp = abs.getATypeParameter() and
sub.getTypeAt(pathToTypeParamInSub) = tp
)
}
/**
* Holds if `term` satisfies the constraint `constraint` with _some_ type
* parameter at `pathToTypeParamInConstraint`, and the type parameter occurs
* at `pathToTypeParamInSub` in a satisfying condition.
*
* Example:
*
* ```rust
* struct MyThing<A> { ... }
*
* trait MyTrait<B> { ... }
*
* impl<T> MyTrait<T> for MyThing<T> { ... }
*
* fn foo<T: MyTrait<i32>>(x: T) { ... }
*
* let x = MyThing(Default::default());
* foo(x);
* ```
*
* At `term` = `foo(x)`, we have `constraint = MyTrait<i32>`, and because of the
* `impl` block, `pathToTypeParamInConstraint` = `"B"`, and
* `pathToTypeParamInSub` = `"A"`.
*/
pragma[nomagic]
private predicate satisfiesConstraintTypeMention(
Term term, Constraint constraint, TypePath path, TypePath pathToTypeParamInSub
) {
satisfiesConstraintTypeMentionInline(term, constraint, _, path, pathToTypeParamInSub)
}
pragma[nomagic]
private predicate satisfiesConstraintTypeMentionThrough(
Term term, Constraint constraint, TypeAbstraction abs, TypePath path,
predicate satisfiesConstraintAtTypeParameter(
Term term, Constraint constraint, TypePath pathToTypeParamInConstraint,
TypePath pathToTypeParamInSub
) {
satisfiesConstraintTypeMentionInline(term, constraint, abs, path, pathToTypeParamInSub)
satisfiesConstraintInline(term, constraint, _, pathToTypeParamInConstraint,
pathToTypeParamInSub, _)
}
pragma[inline]
private predicate satisfiesConstraintTypeNonTypeParamInline(
private predicate satisfiesConstraintNonTypeParamInline(
Term term, TypeAbstraction abs, Constraint constraint, TypePath path, Type t
) {
satisfiesConstraintTypeMention1(term, constraint, abs, _, path, t) and
satisfiesConstraint0(term, constraint, abs, _, path, t) and
not t = abs.getATypeParameter()
}
@@ -1142,15 +1160,14 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
}
/**
* Holds if the type tree at `term` satisfies the constraint `constraint`
* with the type `t` at `path`.
* Holds if `term` satisfies the constraint `constraint` with the type `t` at `path`.
*/
pragma[nomagic]
predicate satisfiesConstraintType(Term term, Constraint constraint, TypePath path, Type t) {
satisfiesConstraintTypeNonTypeParamInline(term, _, constraint, path, t)
predicate satisfiesConstraint(Term term, Constraint constraint, TypePath path, Type t) {
satisfiesConstraintNonTypeParamInline(term, _, constraint, path, t)
or
exists(TypePath prefix0, TypePath pathToTypeParamInSub, TypePath suffix |
satisfiesConstraintTypeMention(term, constraint, prefix0, pathToTypeParamInSub) and
satisfiesConstraintAtTypeParameter(term, constraint, prefix0, pathToTypeParamInSub) and
getTypeAt(term, pathToTypeParamInSub.appendInverse(suffix)) = t and
path = prefix0.append(suffix)
)
@@ -1159,25 +1176,34 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
t = getTypeAt(term, path)
}
pragma[nomagic]
private predicate satisfiesConstraintThrough0(
Term term, Constraint constraint, TypeAbstraction abs, TypePath pathToTypeParamInConstraint,
TypePath pathToTypeParamInSub
) {
satisfiesConstraintInline(term, constraint, abs, pathToTypeParamInConstraint,
pathToTypeParamInSub, _)
}
/**
* Holds if the type tree at `term` satisfies the constraint `constraint`
* through `abs` with the type `t` at `path`.
* Holds if `term` satisfies the constraint `constraint` through `abs` with
* the type `t` at `path`.
*/
pragma[nomagic]
predicate satisfiesConstraintTypeThrough(
predicate satisfiesConstraintThrough(
Term term, TypeAbstraction abs, Constraint constraint, TypePath path, Type t
) {
satisfiesConstraintTypeNonTypeParamInline(term, abs, constraint, path, t)
satisfiesConstraintNonTypeParamInline(term, abs, constraint, path, t)
or
exists(TypePath prefix0, TypePath pathToTypeParamInSub, TypePath suffix |
satisfiesConstraintTypeMentionThrough(term, constraint, abs, prefix0, pathToTypeParamInSub) and
satisfiesConstraintThrough0(term, constraint, abs, prefix0, pathToTypeParamInSub) and
getTypeAt(term, pathToTypeParamInSub.appendInverse(suffix)) = t and
path = prefix0.append(suffix)
)
}
/**
* Holds if the type tree at `term` does _not_ satisfy the constraint `constraint`.
* Holds if `term` does _not_ satisfy the constraint `constraint`.
*
* This is an approximation of `not satisfiesConstraintType(term, constraint, _, _)`,
* but defined without a negative occurrence of `satisfiesConstraintType`.
@@ -1495,7 +1521,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
TypeMention constraint
) {
target = a.getTarget(e) and
typeParameterConstraintHasTypeParameter(target, apos, path, constraint, _, _)
typeParameterHasConstraint(target, apos, _, path, constraint)
}
private newtype TRelevantAccess =
@@ -1556,13 +1582,40 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
SatisfiesTypeParameterConstraintInput>;
pragma[nomagic]
predicate satisfiesConstraintType(
predicate satisfiesConstraintAtTypeParameter(
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
SatisfiesTypeParameterConstraint::satisfiesConstraintAtTypeParameter(ra, constraint,
pathToTypeParamInConstraint, pathToTypeParamInSub) and
constraint = ra.getConstraint(target)
)
}
pragma[nomagic]
predicate satisfiesConstraint(
Access a, AccessEnvironment e, Declaration target, AccessPosition apos, TypePath prefix,
TypeMention constraint, TypePath path, Type t
) {
exists(RelevantAccess ra |
ra = MkRelevantAccess(a, apos, e, prefix) and
SatisfiesTypeParameterConstraint::satisfiesConstraintType(ra, constraint, path, t) 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)
)
}
@@ -1707,7 +1760,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
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::satisfiesConstraintType(a, e, target, apos, pathToTp2, constraint,
AccessConstraint::satisfiesConstraint(a, e, target, apos, pathToTp2, constraint,
pathToTp.appendInverse(path), t)
)
}
@@ -1785,6 +1838,82 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
not result instanceof TypeParameter
)
)
or
exists(
Declaration target, TypePath prefix, TypeMention constraint,
TypePath pathToTypeParamInConstraint, TypePath pathToTypeParamInSub
|
AccessConstraint::satisfiesConstraintAtTypeParameter(a, e, target, apos, prefix,
constraint, pathToTypeParamInConstraint, pathToTypeParamInSub)
|
exists(TypePath suffix |
/*
* Example:
*
* ```rust
* struct MyThing<A> { ... }
*
* trait MyTrait<B> { ... }
*
* impl<T> MyTrait<T> for MyThing<T> { ... }
*
* fn foo<T: MyTrait<i32>>(x: T) { ... }
*
* let x = MyThing(Default::default());
* foo(x);
* ```
*
* At `term` = `foo(x)`, we have
* - `constraint = MyTrait<i32>`,
* - `pathToTypeParamInConstraint` = `"B"`,
* - `pathToTypeParamInSub` = `"A"`,
* - `prefix` = `suffix` = `""`, and
* - `result` = `i32`.
*
* That is, it allows us to infer that the type of `x` is `MyThing<i32>`.
*/
result = constraint.getTypeAt(pathToTypeParamInConstraint.appendInverse(suffix)) and
not result instanceof TypeParameter and
path = prefix.append(pathToTypeParamInSub.append(suffix))
)
or
exists(TypeParameter tp, TypePath suffix, TypePath mid, TypePath pathToTp |
/*
* Example:
*
* ```rust
* struct MyThing<A> { ... }
*
* trait MyTrait<B> { ... }
*
* impl<T> MyTrait<T> for MyThing<T> { ... }
*
* fn bar<T1, T2: MyTrait<T1>>(x: T1, y: T2) {}
*
* let x : i32 = ...;
* let y = MyThing(Default::default());
* bar(x, y);
* ```
*
* At `term` = `bar(x, y)`, we have
* - `constraint = MyTrait<T1>`,
* - `pathToTypeParamInConstraint` = `"B"`,
* - `pathToTypeParamInSub` = `"A"`,
* - `prefix` = `suffix` = `mid` = `""`,
* - `tp = T1`,
* - `pathToTp` = `"B"`, and
* - `result` = `i32`.
*
* That is, it allows us to infer that the type of `y` is `MyThing<i32>`.
*/
typeMatch(a, e, target, suffix, result, tp) and
typeParameterConstraintHasTypeParameter(target, apos, _, constraint, pathToTp, tp) and
pathToTp = pathToTypeParamInConstraint.appendInverse(mid) and
path = prefix.append(pathToTypeParamInSub.append(mid).append(suffix))
)
)
}
}