diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll
index c6a268be126f..4b038f58d31f 100644
--- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll
+++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll
@@ -1718,7 +1718,7 @@ private module AssocFunctionResolution {
* inside `root`.
*/
pragma[nomagic]
- private predicate hasIncompatibleTarget(
+ predicate hasIncompatibleTarget(
ImplOrTraitItemNode i, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow,
Type root
) {
@@ -1743,7 +1743,7 @@ private module AssocFunctionResolution {
* is not satisfied.
*/
pragma[nomagic]
- private predicate hasIncompatibleBlanketLikeTarget(
+ predicate hasIncompatibleBlanketLikeTarget(
ImplItemNode impl, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow
) {
SelfArgIsNotInstantiationOfBlanketLike::argIsNotInstantiationOf(MkAssocFunctionCallCand(this,
@@ -1795,7 +1795,7 @@ private module AssocFunctionResolution {
}
pragma[nomagic]
- private Type getComplexStrippedSelfType(
+ Type getComplexStrippedSelfType(
FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath
) {
result = this.getANonPseudoSelfTypeAt(selfPos, derefChain, borrow, strippedTypePath) and
@@ -1806,258 +1806,25 @@ private module AssocFunctionResolution {
)
}
- bindingset[derefChain, borrow, strippedTypePath, strippedType]
- private predicate hasNoCompatibleNonBlanketLikeTargetCheck(
- FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath,
- Type strippedType
- ) {
- forall(ImplOrTraitItemNode i |
- nonBlanketLikeCandidate(this, _, selfPos, i, _, strippedTypePath, strippedType)
- |
- this.hasIncompatibleTarget(i, selfPos, derefChain, borrow, strippedType)
- )
- }
-
- bindingset[derefChain, borrow, strippedTypePath, strippedType]
- private predicate hasNoCompatibleTargetCheck(
- FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath,
- Type strippedType
- ) {
- this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPos, derefChain, borrow, strippedTypePath,
- strippedType) and
- forall(ImplItemNode i | blanketLikeCandidate(this, _, selfPos, i, _, _, _) |
- this.hasIncompatibleBlanketLikeTarget(i, selfPos, derefChain, borrow)
- )
- }
-
- bindingset[derefChain, borrow, strippedTypePath, strippedType]
- private predicate hasNoCompatibleNonBlanketTargetCheck(
- FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath,
- Type strippedType
- ) {
- this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPos, derefChain, borrow, strippedTypePath,
- strippedType) and
- forall(ImplItemNode i |
- blanketLikeCandidate(this, _, selfPos, i, _, _, _) and
- not i.isBlanketImplementation()
- |
- this.hasIncompatibleBlanketLikeTarget(i, selfPos, derefChain, borrow)
- )
- }
-
- // forex using recursion
- pragma[nomagic]
- private predicate hasNoCompatibleTargetNoBorrowToIndex(
- FunctionPosition selfPos, DerefChain derefChain, TypePath strippedTypePath, Type strippedType,
- int n
- ) {
- this.supportsAutoDerefAndBorrow() and
- this.hasReceiverAtPos(selfPos) and
- strippedType =
- this.getComplexStrippedSelfType(selfPos, derefChain, TNoBorrowKind(), strippedTypePath) and
- n = -1
- or
- this.hasNoCompatibleTargetNoBorrowToIndex(selfPos, derefChain, strippedTypePath, strippedType,
- n - 1) and
- exists(Type t |
- t = getNthLookupType(this, strippedType, n) and
- this.hasNoCompatibleTargetCheck(selfPos, derefChain, TNoBorrowKind(), strippedTypePath, t)
- )
- }
-
- /**
- * Holds if the candidate receiver type represented by `derefChain` does not
- * have a matching call target at function-call adjusted position `selfPos`.
- */
- pragma[nomagic]
- predicate hasNoCompatibleTargetNoBorrow(FunctionPosition selfPos, DerefChain derefChain) {
- exists(Type strippedType |
- this.hasNoCompatibleTargetNoBorrowToIndex(selfPos, derefChain, _, strippedType,
- getLastLookupTypeIndex(this, strippedType))
- )
- }
-
- // forex using recursion
- pragma[nomagic]
- private predicate hasNoCompatibleNonBlanketTargetNoBorrowToIndex(
- FunctionPosition selfPos, DerefChain derefChain, TypePath strippedTypePath, Type strippedType,
- int n
- ) {
- (
- this.supportsAutoDerefAndBorrow() and
- this.hasReceiverAtPos(selfPos)
- or
- // needed for the `hasNoCompatibleNonBlanketTarget` check in
- // `ArgSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate`
- exists(ImplItemNode i |
- derefChain.isEmpty() and
- blanketLikeCandidate(this, _, selfPos, i, _, _, _) and
- i.isBlanketImplementation()
- )
- ) and
- strippedType =
- this.getComplexStrippedSelfType(selfPos, derefChain, TNoBorrowKind(), strippedTypePath) and
- n = -1
- or
- this.hasNoCompatibleNonBlanketTargetNoBorrowToIndex(selfPos, derefChain, strippedTypePath,
- strippedType, n - 1) and
- exists(Type t |
- t = getNthLookupType(this, strippedType, n) and
- this.hasNoCompatibleNonBlanketTargetCheck(selfPos, derefChain, TNoBorrowKind(),
- strippedTypePath, t)
- )
- }
-
- /**
- * Holds if the candidate receiver type represented by `derefChain` does not have
- * a matching non-blanket call target at function-call adjusted position `selfPos`.
- */
- pragma[nomagic]
- predicate hasNoCompatibleNonBlanketTargetNoBorrow(
- FunctionPosition selfPos, DerefChain derefChain
- ) {
- exists(Type strippedType |
- this.hasNoCompatibleNonBlanketTargetNoBorrowToIndex(selfPos, derefChain, _, strippedType,
- getLastLookupTypeIndex(this, strippedType))
- )
- }
-
- // forex using recursion
- pragma[nomagic]
- private predicate hasNoCompatibleTargetSharedBorrowToIndex(
- FunctionPosition selfPos, DerefChain derefChain, TypePath strippedTypePath, Type strippedType,
- int n
- ) {
- this.hasNoCompatibleTargetNoBorrow(selfPos, derefChain) and
- strippedType =
- this.getComplexStrippedSelfType(selfPos, derefChain, TSomeBorrowKind(false),
- strippedTypePath) and
- n = -1
- or
- this.hasNoCompatibleTargetSharedBorrowToIndex(selfPos, derefChain, strippedTypePath,
- strippedType, n - 1) and
- exists(Type t |
- t = getNthLookupType(this, strippedType, n) and
- this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPos, derefChain, TSomeBorrowKind(false),
- strippedTypePath, t)
- )
- }
-
- /**
- * Holds if the candidate receiver type represented by `derefChain`, followed
- * by a shared borrow, does not have a matching call target at function-call
- * adjusted position `selfPos`.
- */
- pragma[nomagic]
- predicate hasNoCompatibleTargetSharedBorrow(FunctionPosition selfPos, DerefChain derefChain) {
- exists(Type strippedType |
- this.hasNoCompatibleTargetSharedBorrowToIndex(selfPos, derefChain, _, strippedType,
- getLastLookupTypeIndex(this, strippedType))
- )
- }
-
- // forex using recursion
- pragma[nomagic]
- private predicate hasNoCompatibleTargetMutBorrowToIndex(
- FunctionPosition selfPos, DerefChain derefChain, TypePath strippedTypePath, Type strippedType,
- int n
- ) {
- this.hasNoCompatibleTargetSharedBorrow(selfPos, derefChain) and
- strippedType =
- this.getComplexStrippedSelfType(selfPos, derefChain, TSomeBorrowKind(true), strippedTypePath) and
- n = -1
- or
- this.hasNoCompatibleTargetMutBorrowToIndex(selfPos, derefChain, strippedTypePath,
- strippedType, n - 1) and
- exists(Type t |
- t = getNthLookupType(this, strippedType, n) and
- this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPos, derefChain, TSomeBorrowKind(true),
- strippedTypePath, t)
- )
- }
-
- /**
- * Holds if the candidate receiver type represented by `derefChain`, followed
- * by a `mut` borrow, does not have a matching call target at function-call
- * adjusted position `selfPos`.
- */
- pragma[nomagic]
- predicate hasNoCompatibleTargetMutBorrow(FunctionPosition selfPos, DerefChain derefChain) {
- exists(Type strippedType |
- this.hasNoCompatibleTargetMutBorrowToIndex(selfPos, derefChain, _, strippedType,
- getLastLookupTypeIndex(this, strippedType))
- )
- }
-
- // forex using recursion
- pragma[nomagic]
- private predicate hasNoCompatibleNonBlanketTargetSharedBorrowToIndex(
- FunctionPosition selfPos, DerefChain derefChain, TypePath strippedTypePath, Type strippedType,
- int n
- ) {
- this.hasNoCompatibleTargetNoBorrow(selfPos, derefChain) and
- strippedType =
- this.getComplexStrippedSelfType(selfPos, derefChain, TSomeBorrowKind(false),
- strippedTypePath) and
- n = -1
- or
- this.hasNoCompatibleNonBlanketTargetSharedBorrowToIndex(selfPos, derefChain, strippedTypePath,
- strippedType, n - 1) and
- exists(Type t |
- t = getNthLookupType(this, strippedType, n) and
- this.hasNoCompatibleNonBlanketTargetCheck(selfPos, derefChain, TSomeBorrowKind(false),
- strippedTypePath, t)
- )
- }
-
/**
- * Holds if the candidate receiver type represented by `derefChain`, followed
- * by a shared borrow, does not have a matching non-blanket call target at
- * function-call adjusted position `selfPos`.
+ * Holds if the candidate receiver type represented by `derefChain` and `borrow`
+ * does not have a matching call target at function-call adjusted position `selfPos`.
*/
- pragma[nomagic]
- predicate hasNoCompatibleNonBlanketTargetSharedBorrow(
- FunctionPosition selfPos, DerefChain derefChain
- ) {
- exists(Type strippedType |
- this.hasNoCompatibleNonBlanketTargetSharedBorrowToIndex(selfPos, derefChain, _,
- strippedType, getLastLookupTypeIndex(this, strippedType))
- )
- }
-
- // forex using recursion
- pragma[nomagic]
- private predicate hasNoCompatibleNonBlanketTargetMutBorrowToIndex(
- FunctionPosition selfPos, DerefChain derefChain, TypePath strippedTypePath, Type strippedType,
- int n
+ predicate hasNoCompatibleTarget(
+ FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow
) {
- this.hasNoCompatibleNonBlanketTargetSharedBorrow(selfPos, derefChain) and
- strippedType =
- this.getComplexStrippedSelfType(selfPos, derefChain, TSomeBorrowKind(true), strippedTypePath) and
- n = -1
- or
- this.hasNoCompatibleNonBlanketTargetMutBorrowToIndex(selfPos, derefChain, strippedTypePath,
- strippedType, n - 1) and
- exists(Type t |
- t = getNthLookupType(this, strippedType, n) and
- this.hasNoCompatibleNonBlanketTargetCheck(selfPos, derefChain, TSomeBorrowKind(true),
- strippedTypePath, t)
- )
+ NoCompatibleTarget::hasNoCompatibleTarget(this, selfPos, derefChain, borrow)
}
/**
- * Holds if the candidate receiver type represented by `derefChain`, followed
- * by a `mut` borrow, does not have a matching non-blanket call target at
- * function-call adjusted position `selfPos`.
+ * Holds if the candidate receiver type represented by `derefChain` and `borrow`
+ * does not have a matching non-blanket call target at function-call adjusted
+ * position `selfPos`.
*/
- pragma[nomagic]
- predicate hasNoCompatibleNonBlanketTargetMutBorrow(
- FunctionPosition selfPos, DerefChain derefChain
+ predicate hasNoCompatibleNonBlanketTarget(
+ FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow
) {
- exists(Type strippedType |
- this.hasNoCompatibleNonBlanketTargetMutBorrowToIndex(selfPos, derefChain, _, strippedType,
- getLastLookupTypeIndex(this, strippedType))
- )
+ NoCompatibleTarget::hasNoCompatibleNonBlanketTarget(this, selfPos, derefChain, borrow)
}
/**
@@ -2082,6 +1849,25 @@ private module AssocFunctionResolution {
)
}
+ /**
+ * Holds if this call may have an implicit borrow of kind `borrow` at
+ * function-call adjusted position `selfPos` with the given `derefChain`.
+ */
+ pragma[nomagic]
+ predicate hasImplicitBorrowCand(
+ FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow
+ ) {
+ exists(BorrowKind prev | this.hasNoCompatibleTarget(selfPos, derefChain, prev) |
+ // first try shared borrow
+ prev.isNoBorrow() and
+ borrow.isSharedBorrow()
+ or
+ // then try mutable borrow
+ prev.isSharedBorrow() and
+ borrow.isMutableBorrow()
+ )
+ }
+
/**
* Gets the type of this call at function-call adjusted position `selfPos` and
* type path `path`.
@@ -2107,23 +1893,15 @@ private module AssocFunctionResolution {
borrow.isNoBorrow()
or
exists(RefType rt |
- // first try shared borrow
- this.hasNoCompatibleTargetNoBorrow(selfPos, derefChain) and
- borrow.isSharedBorrow()
- or
- // then try mutable borrow
- this.hasNoCompatibleTargetSharedBorrow(selfPos, derefChain) and
- borrow.isMutableBorrow()
+ this.hasImplicitBorrowCand(selfPos, derefChain, borrow) and
+ rt = borrow.getRefType()
|
- rt = borrow.getRefType() and
- (
- path.isEmpty() and
- result = rt
- or
- exists(TypePath suffix |
- result = this.getSelfTypeAtNoBorrow(selfPos, derefChain, suffix) and
- path = TypePath::cons(rt.getPositionalTypeParameter(0), suffix)
- )
+ path.isEmpty() and
+ result = rt
+ or
+ exists(TypePath suffix |
+ result = this.getSelfTypeAtNoBorrow(selfPos, derefChain, suffix) and
+ path = TypePath::cons(rt.getPositionalTypeParameter(0), suffix)
)
)
}
@@ -2322,6 +2100,177 @@ private module AssocFunctionResolution {
override Trait getTrait() { result instanceof AnyFnTrait }
}
+ /**
+ * Provides logic for efficiently checking that there are no compatible call
+ * targets for a given candidate receiver type.
+ *
+ * For calls with non-blanket target candidates, we need to check:
+ *
+ * ```text
+ * forall types `t` where `t` is a lookup type for the given candidate receiver type:
+ * forall non-blanket candidates `c` matching `t`:
+ * check that `c` is not a compatible target
+ * ```
+ *
+ * Instead of implementing the above using `forall`, we apply the standard trick
+ * of using ranked recursion.
+ */
+ private module NoCompatibleTarget {
+ private import codeql.rust.elements.internal.generated.Raw
+ private import codeql.rust.elements.internal.generated.Synth
+
+ private class RawImplOrTrait = @impl or @trait;
+
+ private predicate id(RawImplOrTrait x, RawImplOrTrait y) { x = y }
+
+ private predicate idOfRaw(RawImplOrTrait x, int y) = equivalenceRelation(id/2)(x, y)
+
+ private int idOfImplOrTraitItemNode(ImplOrTraitItemNode i) {
+ idOfRaw(Synth::convertAstNodeToRaw(i), result)
+ }
+
+ /**
+ * Holds if `t` is the `n`th lookup type for the candidate receiver type
+ * represented by `derefChain` and `borrow` at function-call adjusted position
+ * `selfPos` of `afc`.
+ *
+ * There are no compatible non-blanket-like candidates for lookup types `0` to `n - 1`.
+ */
+ pragma[nomagic]
+ private predicate noCompatibleNonBlanketLikeTargetCandNthLookupType(
+ AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow,
+ TypePath strippedTypePath, Type strippedType, int n, Type t
+ ) {
+ (
+ (
+ (
+ afc.supportsAutoDerefAndBorrow() and
+ afc.hasReceiverAtPos(selfPos)
+ or
+ // needed for the `hasNoCompatibleNonBlanketTarget` check in
+ // `ArgSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate`
+ exists(ImplItemNode i |
+ derefChain.isEmpty() and
+ blanketLikeCandidate(afc, _, selfPos, i, _, _, _) and
+ i.isBlanketImplementation()
+ )
+ ) and
+ borrow.isNoBorrow()
+ or
+ afc.hasImplicitBorrowCand(selfPos, derefChain, borrow)
+ ) and
+ strippedType = afc.getComplexStrippedSelfType(selfPos, derefChain, borrow, strippedTypePath) and
+ n = 0
+ or
+ hasNoCompatibleNonBlanketLikeTargetForNthLookupType(afc, selfPos, derefChain, borrow,
+ strippedTypePath, strippedType, n - 1)
+ ) and
+ t = getNthLookupType(afc, strippedType, n)
+ }
+
+ pragma[nomagic]
+ private ImplOrTraitItemNode getKthNonBlanketLikeCandidateForNthLookupType(
+ AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow,
+ TypePath strippedTypePath, Type strippedType, int n, Type t, int k
+ ) {
+ noCompatibleNonBlanketLikeTargetCandNthLookupType(afc, selfPos, derefChain, borrow,
+ strippedTypePath, strippedType, n, t) and
+ result =
+ rank[k + 1](ImplOrTraitItemNode i, int id |
+ nonBlanketLikeCandidate(afc, _, selfPos, i, _, strippedTypePath, t) and
+ id = idOfImplOrTraitItemNode(i)
+ |
+ i order by id
+ )
+ }
+
+ pragma[nomagic]
+ private int getLastNonBlanketLikeCandidateForNthLookupType(
+ AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow,
+ TypePath strippedTypePath, Type strippedType, int n
+ ) {
+ exists(Type t |
+ noCompatibleNonBlanketLikeTargetCandNthLookupType(afc, selfPos, derefChain, borrow,
+ strippedTypePath, strippedType, n, t) and
+ result =
+ count(ImplOrTraitItemNode i |
+ nonBlanketLikeCandidate(afc, _, selfPos, i, _, strippedTypePath, t)
+ ) - 1
+ )
+ }
+
+ pragma[nomagic]
+ private predicate hasNoCompatibleNonBlanketLikeTargetForNthLookupTypeToIndex(
+ AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow,
+ TypePath strippedTypePath, Type strippedType, int n, int k
+ ) {
+ exists(Type t |
+ noCompatibleNonBlanketLikeTargetCandNthLookupType(afc, selfPos, derefChain, borrow,
+ strippedTypePath, strippedType, n, t)
+ |
+ k = -1
+ or
+ hasNoCompatibleNonBlanketLikeTargetForNthLookupTypeToIndex(afc, selfPos, derefChain, borrow,
+ strippedTypePath, strippedType, n, k - 1) and
+ exists(ImplOrTraitItemNode i |
+ i =
+ getKthNonBlanketLikeCandidateForNthLookupType(afc, selfPos, derefChain, borrow,
+ strippedTypePath, strippedType, n, t, k) and
+ afc.hasIncompatibleTarget(i, selfPos, derefChain, borrow, t)
+ )
+ )
+ }
+
+ pragma[nomagic]
+ private predicate hasNoCompatibleNonBlanketLikeTargetForNthLookupType(
+ AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow,
+ TypePath strippedTypePath, Type strippedType, int n
+ ) {
+ exists(int last |
+ last =
+ getLastNonBlanketLikeCandidateForNthLookupType(afc, selfPos, derefChain, borrow,
+ strippedTypePath, strippedType, n) and
+ hasNoCompatibleNonBlanketLikeTargetForNthLookupTypeToIndex(afc, selfPos, derefChain, borrow,
+ strippedTypePath, strippedType, n, last)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate hasNoCompatibleNonBlanketLikeTarget(
+ AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow
+ ) {
+ exists(Type strippedType |
+ hasNoCompatibleNonBlanketLikeTargetForNthLookupType(afc, selfPos, derefChain, borrow, _,
+ strippedType, getLastLookupTypeIndex(afc, strippedType))
+ )
+ }
+
+ pragma[nomagic]
+ predicate hasNoCompatibleTarget(
+ AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow
+ ) {
+ hasNoCompatibleNonBlanketLikeTarget(afc, selfPos, derefChain, borrow) and
+ // todo: replace with ranked recursion if needed
+ forall(ImplItemNode i | blanketLikeCandidate(afc, _, selfPos, i, _, _, _) |
+ afc.hasIncompatibleBlanketLikeTarget(i, selfPos, derefChain, borrow)
+ )
+ }
+
+ pragma[nomagic]
+ predicate hasNoCompatibleNonBlanketTarget(
+ AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow
+ ) {
+ hasNoCompatibleNonBlanketLikeTarget(afc, selfPos, derefChain, borrow) and
+ // todo: replace with ranked recursion if needed
+ forall(ImplItemNode i |
+ blanketLikeCandidate(afc, _, selfPos, i, _, _, _) and
+ not i.isBlanketImplementation()
+ |
+ afc.hasIncompatibleBlanketLikeTarget(i, selfPos, derefChain, borrow)
+ )
+ }
+ }
+
pragma[nomagic]
private AssocFunctionDeclaration getAssocFunctionSuccessor(
ImplOrTraitItemNode i, string name, int arity
@@ -2358,14 +2307,7 @@ private module AssocFunctionResolution {
pragma[nomagic]
predicate hasNoCompatibleNonBlanketTarget() {
- afc_.hasNoCompatibleNonBlanketTargetSharedBorrow(selfPos_, derefChain) and
- borrow.isSharedBorrow()
- or
- afc_.hasNoCompatibleNonBlanketTargetMutBorrow(selfPos_, derefChain) and
- borrow.isMutableBorrow()
- or
- afc_.hasNoCompatibleNonBlanketTargetNoBorrow(selfPos_, derefChain) and
- borrow.isNoBorrow()
+ afc_.hasNoCompatibleNonBlanketTarget(selfPos_, derefChain, borrow)
}
pragma[nomagic]
@@ -2474,7 +2416,7 @@ private module AssocFunctionResolution {
MkCallDerefCand(AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain) {
afc.supportsAutoDerefAndBorrow() and
afc.hasReceiverAtPos(selfPos) and
- afc.hasNoCompatibleTargetMutBorrow(selfPos, derefChain) and
+ afc.hasNoCompatibleTarget(selfPos, derefChain, TSomeBorrowKind(true)) and
exists(afc.getSelfTypeAtNoBorrow(selfPos, derefChain, TypePath::nil()))
}
diff --git a/rust/ql/test/library-tests/type-inference/closure.rs b/rust/ql/test/library-tests/type-inference/closure.rs
index fbef401bb083..635b169bf96b 100644
--- a/rust/ql/test/library-tests/type-inference/closure.rs
+++ b/rust/ql/test/library-tests/type-inference/closure.rs
@@ -228,8 +228,16 @@ mod implicit_deref {
let x = 0i32;
let v = Default::default(); // $ type=v:i32 target=default
let s = S(v);
+ let _ret = s(x); // $ type=_ret:bool
let s_ref = &s;
- let _ret = s_ref(x); // $ type=_ret:bool
+ // The call below incorrectly resolves to
+ // `impl FnOnce for &F` from
+ // https://doc.rust-lang.org/std/ops/trait.FnOnce.html#impl-FnOnce%3CA%3E-for-%26F
+ // because `s_ref` gets an implicit borrow `&&S`, and then we incorrectly
+ // conclude that `&S` satisfies the blanket constraint `Fn` because of the
+ // `impl FnOnce for &F` implementation (we do not currently identify that
+ // `&S` does not satisfy `Fn`)
+ let _ret = s_ref(x); // $ MISSING: type=_ret:bool
// The call below is not an implicit deref, instead it will target
// `impl FnOnce for &F` from
diff --git a/rust/ql/test/library-tests/type-inference/type-inference.expected b/rust/ql/test/library-tests/type-inference/type-inference.expected
index 4be703598427..5e870ae6ca5d 100644
--- a/rust/ql/test/library-tests/type-inference/type-inference.expected
+++ b/rust/ql/test/library-tests/type-inference/type-inference.expected
@@ -873,7 +873,7 @@ inferCertainType
| closure.rs:218:13:218:22 | &... | | {EXTERNAL LOCATION} | & |
| closure.rs:218:14:218:22 | \|...\| false | | {EXTERNAL LOCATION} | dyn Fn |
| closure.rs:218:18:218:22 | false | | {EXTERNAL LOCATION} | bool |
-| closure.rs:222:19:240:5 | { ... } | | {EXTERNAL LOCATION} | () |
+| closure.rs:222:19:248:5 | { ... } | | {EXTERNAL LOCATION} | () |
| closure.rs:223:13:223:13 | x | | {EXTERNAL LOCATION} | i64 |
| closure.rs:223:17:223:20 | 0i64 | | {EXTERNAL LOCATION} | i64 |
| closure.rs:226:21:226:23 | ArgList | | {EXTERNAL LOCATION} | (T_1) |
@@ -881,20 +881,23 @@ inferCertainType
| closure.rs:226:22:226:22 | x | | {EXTERNAL LOCATION} | i64 |
| closure.rs:228:13:228:13 | x | | {EXTERNAL LOCATION} | i32 |
| closure.rs:228:17:228:20 | 0i32 | | {EXTERNAL LOCATION} | i32 |
-| closure.rs:231:13:231:17 | s_ref | | {EXTERNAL LOCATION} | & |
-| closure.rs:231:21:231:22 | &s | | {EXTERNAL LOCATION} | & |
-| closure.rs:232:20:232:24 | s_ref | | {EXTERNAL LOCATION} | & |
-| closure.rs:232:25:232:27 | ArgList | | {EXTERNAL LOCATION} | (T_1) |
-| closure.rs:232:25:232:27 | ArgList | T0 | {EXTERNAL LOCATION} | i32 |
-| closure.rs:232:26:232:26 | x | | {EXTERNAL LOCATION} | i32 |
-| closure.rs:238:13:238:13 | c | | {EXTERNAL LOCATION} | dyn Fn |
-| closure.rs:238:17:238:21 | \|...\| x | | {EXTERNAL LOCATION} | dyn Fn |
-| closure.rs:239:9:239:12 | (...) | | {EXTERNAL LOCATION} | & |
-| closure.rs:239:10:239:11 | &c | | {EXTERNAL LOCATION} | & |
-| closure.rs:239:11:239:11 | c | | {EXTERNAL LOCATION} | dyn Fn |
-| closure.rs:239:13:239:15 | ArgList | | {EXTERNAL LOCATION} | (T_1) |
-| closure.rs:239:13:239:15 | ArgList | T0 | {EXTERNAL LOCATION} | i32 |
-| closure.rs:239:14:239:14 | x | | {EXTERNAL LOCATION} | i32 |
+| closure.rs:231:21:231:23 | ArgList | | {EXTERNAL LOCATION} | (T_1) |
+| closure.rs:231:21:231:23 | ArgList | T0 | {EXTERNAL LOCATION} | i32 |
+| closure.rs:231:22:231:22 | x | | {EXTERNAL LOCATION} | i32 |
+| closure.rs:232:13:232:17 | s_ref | | {EXTERNAL LOCATION} | & |
+| closure.rs:232:21:232:22 | &s | | {EXTERNAL LOCATION} | & |
+| closure.rs:240:20:240:24 | s_ref | | {EXTERNAL LOCATION} | & |
+| closure.rs:240:25:240:27 | ArgList | | {EXTERNAL LOCATION} | (T_1) |
+| closure.rs:240:25:240:27 | ArgList | T0 | {EXTERNAL LOCATION} | i32 |
+| closure.rs:240:26:240:26 | x | | {EXTERNAL LOCATION} | i32 |
+| closure.rs:246:13:246:13 | c | | {EXTERNAL LOCATION} | dyn Fn |
+| closure.rs:246:17:246:21 | \|...\| x | | {EXTERNAL LOCATION} | dyn Fn |
+| closure.rs:247:9:247:12 | (...) | | {EXTERNAL LOCATION} | & |
+| closure.rs:247:10:247:11 | &c | | {EXTERNAL LOCATION} | & |
+| closure.rs:247:11:247:11 | c | | {EXTERNAL LOCATION} | dyn Fn |
+| closure.rs:247:13:247:15 | ArgList | | {EXTERNAL LOCATION} | (T_1) |
+| closure.rs:247:13:247:15 | ArgList | T0 | {EXTERNAL LOCATION} | i32 |
+| closure.rs:247:14:247:14 | x | | {EXTERNAL LOCATION} | i32 |
| dereference.rs:13:14:13:18 | SelfParam | | {EXTERNAL LOCATION} | & |
| dereference.rs:13:14:13:18 | SelfParam | TRef | dereference.rs:5:1:7:1 | MyIntPointer |
| dereference.rs:13:29:15:5 | { ... } | | {EXTERNAL LOCATION} | & |
@@ -6894,7 +6897,7 @@ inferType
| closure.rs:218:14:218:22 | \|...\| false | dyn(Output) | {EXTERNAL LOCATION} | bool |
| closure.rs:218:15:218:15 | _ | | closure.rs:214:10:214:10 | T |
| closure.rs:218:18:218:22 | false | | {EXTERNAL LOCATION} | bool |
-| closure.rs:222:19:240:5 | { ... } | | {EXTERNAL LOCATION} | () |
+| closure.rs:222:19:248:5 | { ... } | | {EXTERNAL LOCATION} | () |
| closure.rs:223:13:223:13 | x | | {EXTERNAL LOCATION} | i64 |
| closure.rs:223:17:223:20 | 0i64 | | {EXTERNAL LOCATION} | i64 |
| closure.rs:224:13:224:13 | v | | {EXTERNAL LOCATION} | i64 |
@@ -6920,50 +6923,55 @@ inferType
| closure.rs:230:17:230:20 | S(...) | | closure.rs:212:5:212:19 | S |
| closure.rs:230:17:230:20 | S(...) | T | {EXTERNAL LOCATION} | i32 |
| closure.rs:230:19:230:19 | v | | {EXTERNAL LOCATION} | i32 |
-| closure.rs:231:13:231:17 | s_ref | | {EXTERNAL LOCATION} | & |
-| closure.rs:231:13:231:17 | s_ref | TRef | closure.rs:212:5:212:19 | S |
-| closure.rs:231:13:231:17 | s_ref | TRef.T | {EXTERNAL LOCATION} | i32 |
-| closure.rs:231:21:231:22 | &s | | {EXTERNAL LOCATION} | & |
-| closure.rs:231:21:231:22 | &s | TRef | closure.rs:212:5:212:19 | S |
-| closure.rs:231:21:231:22 | &s | TRef.T | {EXTERNAL LOCATION} | i32 |
-| closure.rs:231:22:231:22 | s | | closure.rs:212:5:212:19 | S |
-| closure.rs:231:22:231:22 | s | T | {EXTERNAL LOCATION} | i32 |
-| closure.rs:232:13:232:16 | _ret | | {EXTERNAL LOCATION} | bool |
-| closure.rs:232:20:232:24 | s_ref | | {EXTERNAL LOCATION} | & |
-| closure.rs:232:20:232:24 | s_ref | TRef | closure.rs:212:5:212:19 | S |
-| closure.rs:232:20:232:24 | s_ref | TRef.T | {EXTERNAL LOCATION} | i32 |
-| closure.rs:232:20:232:27 | s_ref(...) | | {EXTERNAL LOCATION} | bool |
-| closure.rs:232:25:232:27 | ArgList | | {EXTERNAL LOCATION} | (T_1) |
-| closure.rs:232:25:232:27 | ArgList | T0 | {EXTERNAL LOCATION} | i32 |
-| closure.rs:232:26:232:26 | x | | {EXTERNAL LOCATION} | i32 |
-| closure.rs:238:13:238:13 | c | | {EXTERNAL LOCATION} | dyn Fn |
-| closure.rs:238:13:238:13 | c | dyn(Args) | {EXTERNAL LOCATION} | (T_1) |
-| closure.rs:238:13:238:13 | c | dyn(Args).T0 | {EXTERNAL LOCATION} | i32 |
-| closure.rs:238:13:238:13 | c | dyn(Output) | {EXTERNAL LOCATION} | i32 |
-| closure.rs:238:17:238:21 | \|...\| x | | {EXTERNAL LOCATION} | dyn Fn |
-| closure.rs:238:17:238:21 | \|...\| x | dyn(Args) | {EXTERNAL LOCATION} | (T_1) |
-| closure.rs:238:17:238:21 | \|...\| x | dyn(Args).T0 | {EXTERNAL LOCATION} | i32 |
-| closure.rs:238:17:238:21 | \|...\| x | dyn(Output) | {EXTERNAL LOCATION} | i32 |
-| closure.rs:238:18:238:18 | x | | {EXTERNAL LOCATION} | i32 |
-| closure.rs:238:21:238:21 | x | | {EXTERNAL LOCATION} | i32 |
-| closure.rs:239:9:239:12 | (...) | | {EXTERNAL LOCATION} | & |
-| closure.rs:239:9:239:12 | (...) | TRef | {EXTERNAL LOCATION} | dyn Fn |
-| closure.rs:239:9:239:12 | (...) | TRef.dyn(Args) | {EXTERNAL LOCATION} | (T_1) |
-| closure.rs:239:9:239:12 | (...) | TRef.dyn(Args).T0 | {EXTERNAL LOCATION} | i32 |
-| closure.rs:239:9:239:12 | (...) | TRef.dyn(Output) | {EXTERNAL LOCATION} | i32 |
-| closure.rs:239:9:239:15 | ...(...) | | {EXTERNAL LOCATION} | i32 |
-| closure.rs:239:10:239:11 | &c | | {EXTERNAL LOCATION} | & |
-| closure.rs:239:10:239:11 | &c | TRef | {EXTERNAL LOCATION} | dyn Fn |
-| closure.rs:239:10:239:11 | &c | TRef.dyn(Args) | {EXTERNAL LOCATION} | (T_1) |
-| closure.rs:239:10:239:11 | &c | TRef.dyn(Args).T0 | {EXTERNAL LOCATION} | i32 |
-| closure.rs:239:10:239:11 | &c | TRef.dyn(Output) | {EXTERNAL LOCATION} | i32 |
-| closure.rs:239:11:239:11 | c | | {EXTERNAL LOCATION} | dyn Fn |
-| closure.rs:239:11:239:11 | c | dyn(Args) | {EXTERNAL LOCATION} | (T_1) |
-| closure.rs:239:11:239:11 | c | dyn(Args).T0 | {EXTERNAL LOCATION} | i32 |
-| closure.rs:239:11:239:11 | c | dyn(Output) | {EXTERNAL LOCATION} | i32 |
-| closure.rs:239:13:239:15 | ArgList | | {EXTERNAL LOCATION} | (T_1) |
-| closure.rs:239:13:239:15 | ArgList | T0 | {EXTERNAL LOCATION} | i32 |
-| closure.rs:239:14:239:14 | x | | {EXTERNAL LOCATION} | i32 |
+| closure.rs:231:13:231:16 | _ret | | {EXTERNAL LOCATION} | bool |
+| closure.rs:231:20:231:20 | s | | closure.rs:212:5:212:19 | S |
+| closure.rs:231:20:231:20 | s | T | {EXTERNAL LOCATION} | i32 |
+| closure.rs:231:20:231:23 | s(...) | | {EXTERNAL LOCATION} | bool |
+| closure.rs:231:21:231:23 | ArgList | | {EXTERNAL LOCATION} | (T_1) |
+| closure.rs:231:21:231:23 | ArgList | T0 | {EXTERNAL LOCATION} | i32 |
+| closure.rs:231:22:231:22 | x | | {EXTERNAL LOCATION} | i32 |
+| closure.rs:232:13:232:17 | s_ref | | {EXTERNAL LOCATION} | & |
+| closure.rs:232:13:232:17 | s_ref | TRef | closure.rs:212:5:212:19 | S |
+| closure.rs:232:13:232:17 | s_ref | TRef.T | {EXTERNAL LOCATION} | i32 |
+| closure.rs:232:21:232:22 | &s | | {EXTERNAL LOCATION} | & |
+| closure.rs:232:21:232:22 | &s | TRef | closure.rs:212:5:212:19 | S |
+| closure.rs:232:21:232:22 | &s | TRef.T | {EXTERNAL LOCATION} | i32 |
+| closure.rs:232:22:232:22 | s | | closure.rs:212:5:212:19 | S |
+| closure.rs:232:22:232:22 | s | T | {EXTERNAL LOCATION} | i32 |
+| closure.rs:240:20:240:24 | s_ref | | {EXTERNAL LOCATION} | & |
+| closure.rs:240:20:240:24 | s_ref | TRef | closure.rs:212:5:212:19 | S |
+| closure.rs:240:20:240:24 | s_ref | TRef.T | {EXTERNAL LOCATION} | i32 |
+| closure.rs:240:25:240:27 | ArgList | | {EXTERNAL LOCATION} | (T_1) |
+| closure.rs:240:25:240:27 | ArgList | T0 | {EXTERNAL LOCATION} | i32 |
+| closure.rs:240:26:240:26 | x | | {EXTERNAL LOCATION} | i32 |
+| closure.rs:246:13:246:13 | c | | {EXTERNAL LOCATION} | dyn Fn |
+| closure.rs:246:13:246:13 | c | dyn(Args) | {EXTERNAL LOCATION} | (T_1) |
+| closure.rs:246:13:246:13 | c | dyn(Args).T0 | {EXTERNAL LOCATION} | i32 |
+| closure.rs:246:13:246:13 | c | dyn(Output) | {EXTERNAL LOCATION} | i32 |
+| closure.rs:246:17:246:21 | \|...\| x | | {EXTERNAL LOCATION} | dyn Fn |
+| closure.rs:246:17:246:21 | \|...\| x | dyn(Args) | {EXTERNAL LOCATION} | (T_1) |
+| closure.rs:246:17:246:21 | \|...\| x | dyn(Args).T0 | {EXTERNAL LOCATION} | i32 |
+| closure.rs:246:17:246:21 | \|...\| x | dyn(Output) | {EXTERNAL LOCATION} | i32 |
+| closure.rs:246:18:246:18 | x | | {EXTERNAL LOCATION} | i32 |
+| closure.rs:246:21:246:21 | x | | {EXTERNAL LOCATION} | i32 |
+| closure.rs:247:9:247:12 | (...) | | {EXTERNAL LOCATION} | & |
+| closure.rs:247:9:247:12 | (...) | TRef | {EXTERNAL LOCATION} | dyn Fn |
+| closure.rs:247:9:247:12 | (...) | TRef.dyn(Args) | {EXTERNAL LOCATION} | (T_1) |
+| closure.rs:247:9:247:12 | (...) | TRef.dyn(Args).T0 | {EXTERNAL LOCATION} | i32 |
+| closure.rs:247:9:247:12 | (...) | TRef.dyn(Output) | {EXTERNAL LOCATION} | i32 |
+| closure.rs:247:9:247:15 | ...(...) | | {EXTERNAL LOCATION} | i32 |
+| closure.rs:247:10:247:11 | &c | | {EXTERNAL LOCATION} | & |
+| closure.rs:247:10:247:11 | &c | TRef | {EXTERNAL LOCATION} | dyn Fn |
+| closure.rs:247:10:247:11 | &c | TRef.dyn(Args) | {EXTERNAL LOCATION} | (T_1) |
+| closure.rs:247:10:247:11 | &c | TRef.dyn(Args).T0 | {EXTERNAL LOCATION} | i32 |
+| closure.rs:247:10:247:11 | &c | TRef.dyn(Output) | {EXTERNAL LOCATION} | i32 |
+| closure.rs:247:11:247:11 | c | | {EXTERNAL LOCATION} | dyn Fn |
+| closure.rs:247:11:247:11 | c | dyn(Args) | {EXTERNAL LOCATION} | (T_1) |
+| closure.rs:247:11:247:11 | c | dyn(Args).T0 | {EXTERNAL LOCATION} | i32 |
+| closure.rs:247:11:247:11 | c | dyn(Output) | {EXTERNAL LOCATION} | i32 |
+| closure.rs:247:13:247:15 | ArgList | | {EXTERNAL LOCATION} | (T_1) |
+| closure.rs:247:13:247:15 | ArgList | T0 | {EXTERNAL LOCATION} | i32 |
+| closure.rs:247:14:247:14 | x | | {EXTERNAL LOCATION} | i32 |
| dereference.rs:13:14:13:18 | SelfParam | | {EXTERNAL LOCATION} | & |
| dereference.rs:13:14:13:18 | SelfParam | TRef | dereference.rs:5:1:7:1 | MyIntPointer |
| dereference.rs:13:29:15:5 | { ... } | | {EXTERNAL LOCATION} | & |