From 42fe2d5002f5cad99ce5d92146fca9a23f723e39 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 10 Apr 2026 10:08:09 +0200 Subject: [PATCH 1/3] Rust: Add another type inference test --- .../library-tests/type-inference/closure.rs | 1 + .../type-inference/type-inference.expected | 130 ++++++++++-------- 2 files changed, 71 insertions(+), 60 deletions(-) diff --git a/rust/ql/test/library-tests/type-inference/closure.rs b/rust/ql/test/library-tests/type-inference/closure.rs index fbef401bb083..a63a708e2456 100644 --- a/rust/ql/test/library-tests/type-inference/closure.rs +++ b/rust/ql/test/library-tests/type-inference/closure.rs @@ -228,6 +228,7 @@ 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 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..ceae6f4dd095 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:241: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:233:20:233:24 | s_ref | | {EXTERNAL LOCATION} | & | +| closure.rs:233:25:233:27 | ArgList | | {EXTERNAL LOCATION} | (T_1) | +| closure.rs:233:25:233:27 | ArgList | T0 | {EXTERNAL LOCATION} | i32 | +| closure.rs:233:26:233:26 | x | | {EXTERNAL LOCATION} | i32 | +| closure.rs:239:13:239:13 | c | | {EXTERNAL LOCATION} | dyn Fn | +| closure.rs:239:17:239:21 | \|...\| x | | {EXTERNAL LOCATION} | dyn Fn | +| closure.rs:240:9:240:12 | (...) | | {EXTERNAL LOCATION} | & | +| closure.rs:240:10:240:11 | &c | | {EXTERNAL LOCATION} | & | +| closure.rs:240:11:240:11 | c | | {EXTERNAL LOCATION} | dyn Fn | +| closure.rs:240:13:240:15 | ArgList | | {EXTERNAL LOCATION} | (T_1) | +| closure.rs:240:13:240:15 | ArgList | T0 | {EXTERNAL LOCATION} | i32 | +| closure.rs:240:14:240: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:241: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,57 @@ 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:233:13:233:16 | _ret | | {EXTERNAL LOCATION} | bool | +| closure.rs:233:20:233:24 | s_ref | | {EXTERNAL LOCATION} | & | +| closure.rs:233:20:233:24 | s_ref | TRef | closure.rs:212:5:212:19 | S | +| closure.rs:233:20:233:24 | s_ref | TRef.T | {EXTERNAL LOCATION} | i32 | +| closure.rs:233:20:233:27 | s_ref(...) | | {EXTERNAL LOCATION} | bool | +| closure.rs:233:25:233:27 | ArgList | | {EXTERNAL LOCATION} | (T_1) | +| closure.rs:233:25:233:27 | ArgList | T0 | {EXTERNAL LOCATION} | i32 | +| closure.rs:233:26:233:26 | x | | {EXTERNAL LOCATION} | i32 | +| closure.rs:239:13:239:13 | c | | {EXTERNAL LOCATION} | dyn Fn | +| closure.rs:239:13:239:13 | c | dyn(Args) | {EXTERNAL LOCATION} | (T_1) | +| closure.rs:239:13:239:13 | c | dyn(Args).T0 | {EXTERNAL LOCATION} | i32 | +| closure.rs:239:13:239:13 | c | dyn(Output) | {EXTERNAL LOCATION} | i32 | +| closure.rs:239:17:239:21 | \|...\| x | | {EXTERNAL LOCATION} | dyn Fn | +| closure.rs:239:17:239:21 | \|...\| x | dyn(Args) | {EXTERNAL LOCATION} | (T_1) | +| closure.rs:239:17:239:21 | \|...\| x | dyn(Args).T0 | {EXTERNAL LOCATION} | i32 | +| closure.rs:239:17:239:21 | \|...\| x | dyn(Output) | {EXTERNAL LOCATION} | i32 | +| closure.rs:239:18:239:18 | x | | {EXTERNAL LOCATION} | i32 | +| closure.rs:239:21:239:21 | x | | {EXTERNAL LOCATION} | i32 | +| closure.rs:240:9:240:12 | (...) | | {EXTERNAL LOCATION} | & | +| closure.rs:240:9:240:12 | (...) | TRef | {EXTERNAL LOCATION} | dyn Fn | +| closure.rs:240:9:240:12 | (...) | TRef.dyn(Args) | {EXTERNAL LOCATION} | (T_1) | +| closure.rs:240:9:240:12 | (...) | TRef.dyn(Args).T0 | {EXTERNAL LOCATION} | i32 | +| closure.rs:240:9:240:12 | (...) | TRef.dyn(Output) | {EXTERNAL LOCATION} | i32 | +| closure.rs:240:9:240:15 | ...(...) | | {EXTERNAL LOCATION} | i32 | +| closure.rs:240:10:240:11 | &c | | {EXTERNAL LOCATION} | & | +| closure.rs:240:10:240:11 | &c | TRef | {EXTERNAL LOCATION} | dyn Fn | +| closure.rs:240:10:240:11 | &c | TRef.dyn(Args) | {EXTERNAL LOCATION} | (T_1) | +| closure.rs:240:10:240:11 | &c | TRef.dyn(Args).T0 | {EXTERNAL LOCATION} | i32 | +| closure.rs:240:10:240:11 | &c | TRef.dyn(Output) | {EXTERNAL LOCATION} | i32 | +| closure.rs:240:11:240:11 | c | | {EXTERNAL LOCATION} | dyn Fn | +| closure.rs:240:11:240:11 | c | dyn(Args) | {EXTERNAL LOCATION} | (T_1) | +| closure.rs:240:11:240:11 | c | dyn(Args).T0 | {EXTERNAL LOCATION} | i32 | +| closure.rs:240:11:240:11 | c | dyn(Output) | {EXTERNAL LOCATION} | i32 | +| closure.rs:240:13:240:15 | ArgList | | {EXTERNAL LOCATION} | (T_1) | +| closure.rs:240:13:240:15 | ArgList | T0 | {EXTERNAL LOCATION} | i32 | +| closure.rs:240:14:240: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} | & | From be329c8ab4aba6956d8eeddb887401c71e191037 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 9 Apr 2026 13:23:46 +0200 Subject: [PATCH 2/3] Rust: Replace recursion through `forall` with ranked recursion --- .../internal/typeinference/TypeInference.qll | 490 ++++++++---------- .../dataflow/local/DataFlowStep.expected | 5 - .../library-tests/type-inference/closure.rs | 9 +- .../type-inference/type-inference.expected | 98 ++-- 4 files changed, 270 insertions(+), 332 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index c6a268be126f..cc6e13d59217 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`. + * 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 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`. - */ - 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())) } @@ -2547,10 +2489,6 @@ private module AssocFunctionResolution { // (https://rust-lang.github.io/rfcs/1210-impl-specialization.html), as well as // cases where our blanket implementation filtering is not precise enough. if impl.isBlanketImplementation() then afcc.hasNoCompatibleNonBlanketTarget() else any() - | - borrow.isNoBorrow() - or - blanketPath.getHead() = borrow.getRefType().getPositionalTypeParameter(0) ) } } diff --git a/rust/ql/test/library-tests/dataflow/local/DataFlowStep.expected b/rust/ql/test/library-tests/dataflow/local/DataFlowStep.expected index b0c11770a8e6..e220a769eceb 100644 --- a/rust/ql/test/library-tests/dataflow/local/DataFlowStep.expected +++ b/rust/ql/test/library-tests/dataflow/local/DataFlowStep.expected @@ -927,9 +927,6 @@ readStep | main.rs:480:25:480:29 | names | file://:0:0:0:0 | element | main.rs:480:9:480:20 | TuplePat | | main.rs:482:41:482:67 | [post] \|...\| ... | main.rs:479:9:479:20 | captured default_name | main.rs:482:41:482:67 | [post] default_name | | main.rs:482:44:482:55 | [post] default_name [implicit borrow] | file://:0:0:0:0 | &ref | main.rs:482:44:482:55 | [post] default_name | -| main.rs:482:44:482:55 | [post] default_name [implicit borrow] | file://:0:0:0:0 | &ref | main.rs:482:44:482:55 | [post] default_name [implicit deref 0 in state after deref] | -| main.rs:482:44:482:55 | [post] default_name [implicit deref 0 in state after borrow] | file://:0:0:0:0 | &ref | main.rs:482:44:482:55 | [post] default_name | -| main.rs:482:44:482:55 | default_name [implicit deref 0 in state before deref] | file://:0:0:0:0 | &ref | main.rs:482:44:482:55 | default_name [implicit deref 0 in state after deref] | | main.rs:482:44:482:55 | this | main.rs:479:9:479:20 | captured default_name | main.rs:482:44:482:55 | default_name | | main.rs:483:18:483:18 | [post] n [implicit borrow] | file://:0:0:0:0 | &ref | main.rs:483:18:483:18 | [post] n | | main.rs:506:13:506:13 | [post] a [implicit borrow] | file://:0:0:0:0 | &ref | main.rs:506:13:506:13 | [post] a | @@ -1056,8 +1053,6 @@ storeStep | main.rs:479:24:479:33 | source(...) | file://:0:0:0:0 | &ref | main.rs:479:24:479:33 | source(...) [implicit borrow] | | main.rs:482:41:482:67 | default_name | main.rs:479:9:479:20 | captured default_name | main.rs:482:41:482:67 | \|...\| ... | | main.rs:482:44:482:55 | default_name | file://:0:0:0:0 | &ref | main.rs:482:44:482:55 | default_name [implicit borrow] | -| main.rs:482:44:482:55 | default_name | file://:0:0:0:0 | &ref | main.rs:482:44:482:55 | default_name [implicit deref 0 in state after borrow] | -| main.rs:482:44:482:55 | default_name [implicit deref 0 in state after deref] | file://:0:0:0:0 | &ref | main.rs:482:44:482:55 | default_name [implicit borrow] | | main.rs:483:18:483:18 | n | file://:0:0:0:0 | &ref | main.rs:483:18:483:18 | n [implicit borrow] | | main.rs:506:13:506:13 | a | file://:0:0:0:0 | &ref | main.rs:506:13:506:13 | a [implicit borrow] | | main.rs:507:13:507:13 | b | file://:0:0:0:0 | &ref | main.rs:507:13:507:13 | b [implicit deref 0 in state after borrow] | diff --git a/rust/ql/test/library-tests/type-inference/closure.rs b/rust/ql/test/library-tests/type-inference/closure.rs index a63a708e2456..635b169bf96b 100644 --- a/rust/ql/test/library-tests/type-inference/closure.rs +++ b/rust/ql/test/library-tests/type-inference/closure.rs @@ -230,7 +230,14 @@ mod implicit_deref { 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 ceae6f4dd095..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:241: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) | @@ -886,18 +886,18 @@ inferCertainType | 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:233:20:233:24 | s_ref | | {EXTERNAL LOCATION} | & | -| closure.rs:233:25:233:27 | ArgList | | {EXTERNAL LOCATION} | (T_1) | -| closure.rs:233:25:233:27 | ArgList | T0 | {EXTERNAL LOCATION} | i32 | -| closure.rs:233:26:233:26 | x | | {EXTERNAL LOCATION} | i32 | -| closure.rs:239:13:239:13 | c | | {EXTERNAL LOCATION} | dyn Fn | -| closure.rs:239:17:239:21 | \|...\| x | | {EXTERNAL LOCATION} | dyn Fn | -| closure.rs:240:9:240:12 | (...) | | {EXTERNAL LOCATION} | & | -| closure.rs:240:10:240:11 | &c | | {EXTERNAL LOCATION} | & | -| closure.rs:240:11:240:11 | c | | {EXTERNAL LOCATION} | dyn Fn | -| closure.rs:240:13:240:15 | ArgList | | {EXTERNAL LOCATION} | (T_1) | -| closure.rs:240:13:240:15 | ArgList | T0 | {EXTERNAL LOCATION} | i32 | -| closure.rs:240:14:240:14 | x | | {EXTERNAL LOCATION} | i32 | +| 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} | & | @@ -6897,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:241: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 | @@ -6938,42 +6938,40 @@ inferType | 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:233:13:233:16 | _ret | | {EXTERNAL LOCATION} | bool | -| closure.rs:233:20:233:24 | s_ref | | {EXTERNAL LOCATION} | & | -| closure.rs:233:20:233:24 | s_ref | TRef | closure.rs:212:5:212:19 | S | -| closure.rs:233:20:233:24 | s_ref | TRef.T | {EXTERNAL LOCATION} | i32 | -| closure.rs:233:20:233:27 | s_ref(...) | | {EXTERNAL LOCATION} | bool | -| closure.rs:233:25:233:27 | ArgList | | {EXTERNAL LOCATION} | (T_1) | -| closure.rs:233:25:233:27 | ArgList | T0 | {EXTERNAL LOCATION} | i32 | -| closure.rs:233:26:233:26 | x | | {EXTERNAL LOCATION} | i32 | -| closure.rs:239:13:239:13 | c | | {EXTERNAL LOCATION} | dyn Fn | -| closure.rs:239:13:239:13 | c | dyn(Args) | {EXTERNAL LOCATION} | (T_1) | -| closure.rs:239:13:239:13 | c | dyn(Args).T0 | {EXTERNAL LOCATION} | i32 | -| closure.rs:239:13:239:13 | c | dyn(Output) | {EXTERNAL LOCATION} | i32 | -| closure.rs:239:17:239:21 | \|...\| x | | {EXTERNAL LOCATION} | dyn Fn | -| closure.rs:239:17:239:21 | \|...\| x | dyn(Args) | {EXTERNAL LOCATION} | (T_1) | -| closure.rs:239:17:239:21 | \|...\| x | dyn(Args).T0 | {EXTERNAL LOCATION} | i32 | -| closure.rs:239:17:239:21 | \|...\| x | dyn(Output) | {EXTERNAL LOCATION} | i32 | -| closure.rs:239:18:239:18 | x | | {EXTERNAL LOCATION} | i32 | -| closure.rs:239:21:239:21 | x | | {EXTERNAL LOCATION} | i32 | -| closure.rs:240:9:240:12 | (...) | | {EXTERNAL LOCATION} | & | -| closure.rs:240:9:240:12 | (...) | TRef | {EXTERNAL LOCATION} | dyn Fn | -| closure.rs:240:9:240:12 | (...) | TRef.dyn(Args) | {EXTERNAL LOCATION} | (T_1) | -| closure.rs:240:9:240:12 | (...) | TRef.dyn(Args).T0 | {EXTERNAL LOCATION} | i32 | -| closure.rs:240:9:240:12 | (...) | TRef.dyn(Output) | {EXTERNAL LOCATION} | i32 | -| closure.rs:240:9:240:15 | ...(...) | | {EXTERNAL LOCATION} | i32 | -| closure.rs:240:10:240:11 | &c | | {EXTERNAL LOCATION} | & | -| closure.rs:240:10:240:11 | &c | TRef | {EXTERNAL LOCATION} | dyn Fn | -| closure.rs:240:10:240:11 | &c | TRef.dyn(Args) | {EXTERNAL LOCATION} | (T_1) | -| closure.rs:240:10:240:11 | &c | TRef.dyn(Args).T0 | {EXTERNAL LOCATION} | i32 | -| closure.rs:240:10:240:11 | &c | TRef.dyn(Output) | {EXTERNAL LOCATION} | i32 | -| closure.rs:240:11:240:11 | c | | {EXTERNAL LOCATION} | dyn Fn | -| closure.rs:240:11:240:11 | c | dyn(Args) | {EXTERNAL LOCATION} | (T_1) | -| closure.rs:240:11:240:11 | c | dyn(Args).T0 | {EXTERNAL LOCATION} | i32 | -| closure.rs:240:11:240:11 | c | dyn(Output) | {EXTERNAL LOCATION} | i32 | -| closure.rs:240:13:240:15 | ArgList | | {EXTERNAL LOCATION} | (T_1) | -| closure.rs:240:13:240:15 | ArgList | T0 | {EXTERNAL LOCATION} | i32 | -| closure.rs:240:14:240:14 | x | | {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} | & | From 27f7f747a414635cfc0413898549cd98a7433938 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 10 Apr 2026 13:20:36 +0200 Subject: [PATCH 3/3] Rust: Check whole blanket constraints, not just the root trait type --- .../codeql/rust/internal/PathResolution.qll | 15 ++++++----- .../typeinference/BlanketImplementation.qll | 27 ++++++++++--------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/internal/PathResolution.qll index c38e9bff91c2..10d18786880b 100644 --- a/rust/ql/lib/codeql/rust/internal/PathResolution.qll +++ b/rust/ql/lib/codeql/rust/internal/PathResolution.qll @@ -1202,18 +1202,21 @@ final class TypeParamItemNode extends NamedItemNode, TypeItemNode instanceof Typ } pragma[nomagic] - Path getABoundPath() { result = this.getTypeBoundAt(_, _).getTypeRepr().(PathTypeRepr).getPath() } - - pragma[nomagic] - ItemNode resolveBound(int index) { + Path getBoundPath(int index) { result = rank[index + 1](int i, int j | | - resolvePath(this.getTypeBoundAt(i, j).getTypeRepr().(PathTypeRepr).getPath()) order by i, j + this.getTypeBoundAt(i, j).getTypeRepr().(PathTypeRepr).getPath() order by i, j ) } - ItemNode resolveABound() { result = resolvePath(this.getABoundPath()) } + pragma[nomagic] + Path getABoundPath() { result = this.getBoundPath(_) } + + pragma[nomagic] + ItemNode resolveBound(int index) { result = resolvePath(this.getBoundPath(index)) } + + ItemNode resolveABound() { result = this.resolveBound(_) } pragma[nomagic] ItemNode resolveAdditionalBound(ItemNode constrainingItem) { diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/BlanketImplementation.qll b/rust/ql/lib/codeql/rust/internal/typeinference/BlanketImplementation.qll index 26e8bdea4e0e..86bcdbe4fe8f 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/BlanketImplementation.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/BlanketImplementation.qll @@ -15,10 +15,11 @@ private import TypeInference * Holds if `traitBound` is the first non-trivial trait bound of `tp`. */ pragma[nomagic] -private predicate hasFirstNonTrivialTraitBound(TypeParamItemNode tp, Trait traitBound) { +private predicate hasFirstNonTrivialTraitBound(TypeParamItemNode tp, Path traitBound) { traitBound = - min(Trait trait, int i | - trait = tp.resolveBound(i) and + min(Trait trait, Path path, int i | + path = tp.getBoundPath(i) and + trait = resolvePath(path) and // Exclude traits that are known to not narrow things down very much. not trait.getName().getText() = [ @@ -27,7 +28,7 @@ private predicate hasFirstNonTrivialTraitBound(TypeParamItemNode tp, Trait trait "Send", "Sync", "Unpin", "UnwindSafe", "RefUnwindSafe" ] | - trait order by i + path order by i ) } @@ -103,11 +104,11 @@ module SatisfiesBlanketConstraint< } private module SatisfiesBlanketConstraintInput implements - SatisfiesTypeInputSig + SatisfiesConstraintInputSig { pragma[nomagic] additional predicate relevantConstraint( - ArgumentTypeAndBlanketOffset ato, ImplItemNode impl, Trait traitBound + ArgumentTypeAndBlanketOffset ato, ImplItemNode impl, Path traitBound ) { exists(ArgumentType at, TypePath blanketPath, TypeParam blanketTypeParam | ato = MkArgumentTypeAndBlanketOffset(at, blanketPath) and @@ -117,13 +118,13 @@ module SatisfiesBlanketConstraint< } pragma[nomagic] - predicate relevantConstraint(ArgumentTypeAndBlanketOffset ato, Type constraint) { - relevantConstraint(ato, _, constraint.(TraitType).getTrait()) + predicate relevantConstraint(ArgumentTypeAndBlanketOffset ato, TypeMention constraint) { + relevantConstraint(ato, _, constraint) } } private module SatisfiesBlanketConstraint = - SatisfiesType; + SatisfiesConstraint; /** * Holds if the argument type `at` satisfies the first non-trivial blanket @@ -131,10 +132,10 @@ module SatisfiesBlanketConstraint< */ pragma[nomagic] predicate satisfiesBlanketConstraint(ArgumentType at, ImplItemNode impl) { - exists(ArgumentTypeAndBlanketOffset ato, Trait traitBound | + exists(ArgumentTypeAndBlanketOffset ato, Path traitBound | ato = MkArgumentTypeAndBlanketOffset(at, _) and SatisfiesBlanketConstraintInput::relevantConstraint(ato, impl, traitBound) and - SatisfiesBlanketConstraint::satisfiesConstraint(ato, TTrait(traitBound), _, _) + SatisfiesBlanketConstraint::satisfiesConstraint(ato, traitBound, _, _) ) or exists(TypeParam blanketTypeParam | @@ -149,10 +150,10 @@ module SatisfiesBlanketConstraint< */ pragma[nomagic] predicate dissatisfiesBlanketConstraint(ArgumentType at, ImplItemNode impl) { - exists(ArgumentTypeAndBlanketOffset ato, Trait traitBound | + exists(ArgumentTypeAndBlanketOffset ato, Path traitBound | ato = MkArgumentTypeAndBlanketOffset(at, _) and SatisfiesBlanketConstraintInput::relevantConstraint(ato, impl, traitBound) and - SatisfiesBlanketConstraint::dissatisfiesConstraint(ato, TTrait(traitBound)) + SatisfiesBlanketConstraint::dissatisfiesConstraint(ato, traitBound) ) } }