diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index c6a268be126f..9eb72ef1c869 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,36 @@ 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`. + * 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 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 + predicate hasNoCompatibleTarget( + FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow ) { - 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) - ) + NoCompatibleTarget::hasNoCompatibleTarget(this, selfPos, derefChain, borrow) } /** - * 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 non-blanket-like 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 hasNoCompatibleNonBlanketLikeTarget( + 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::hasNoCompatibleNonBlanketLikeTarget(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 +1860,27 @@ 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 | + // first try shared borrow + this.hasNoCompatibleTarget(selfPos, derefChain, prev) and + prev.isNoBorrow() and + borrow.isSharedBorrow() + or + // then try mutable borrow + this.hasNoCompatibleNonBlanketLikeTarget(selfPos, derefChain, prev) and + prev.isSharedBorrow() and + borrow.isMutableBorrow() + ) + } + /** * Gets the type of this call at function-call adjusted position `selfPos` and * type path `path`. @@ -2107,23 +1906,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 +2113,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] + 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 +2320,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 +2429,7 @@ private module AssocFunctionResolution { MkCallDerefCand(AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain) { afc.supportsAutoDerefAndBorrow() and afc.hasReceiverAtPos(selfPos) and - afc.hasNoCompatibleTargetMutBorrow(selfPos, derefChain) and + afc.hasNoCompatibleNonBlanketLikeTarget(selfPos, derefChain, TSomeBorrowKind(true)) and exists(afc.getSelfTypeAtNoBorrow(selfPos, derefChain, TypePath::nil())) }