Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 137 additions & 42 deletions rust/ql/lib/codeql/rust/internal/TypeInference.qll
Original file line number Diff line number Diff line change
Expand Up @@ -901,14 +901,14 @@

/**
* Holds if function `f` with the name `name` and the arity `arity` exists in
* blanket implementation `impl` of `trait`, and the type at position
* blanket (like) implementation `impl` of `trait`, and the type at position
* `pos` is `t`.
*
* `blanketPath` points to the type `blanketTypeParam` inside `t`, which
* is the type parameter used in the blanket implementation.
*/
pragma[nomagic]
private predicate functionInfoBlanket(
private predicate functionInfoBlanketLike(
Function f, string name, int arity, ImplItemNode impl, Trait trait, FunctionPosition pos,
AssocFunctionType t, TypePath blanketPath, TypeParam blanketTypeParam
) {
Expand Down Expand Up @@ -1027,19 +1027,20 @@

/**
* Holds if method `m` with the name `name` and the arity `arity` exists in
* blanket implementation `impl` of `trait`, and the type of the `self`
* blanket (like) implementation `impl` of `trait`, and the type of the `self`
* parameter is `selfType`.
*
* `blanketPath` points to the type `blanketTypeParam` inside `selfType`, which
* is the type parameter used in the blanket implementation.
*/
pragma[nomagic]
private predicate methodInfoBlanket(
private predicate methodInfoBlanketLike(
Method m, string name, int arity, ImplItemNode impl, Trait trait, AssocFunctionType selfType,
TypePath blanketPath, TypeParam blanketTypeParam
) {
exists(FunctionPosition pos |
functionInfoBlanket(m, name, arity, impl, trait, pos, selfType, blanketPath, blanketTypeParam) and
functionInfoBlanketLike(m, name, arity, impl, trait, pos, selfType, blanketPath,
blanketTypeParam) and
Comment on lines +1042 to +1043
Copy link

Copilot AI Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The line break in the middle of the function call arguments reduces readability. Consider keeping all parameters on the same line or formatting consistently with other similar calls in the file.

Suggested change
functionInfoBlanketLike(m, name, arity, impl, trait, pos, selfType, blanketPath,
blanketTypeParam) and
functionInfoBlanketLike(m, name, arity, impl, trait, pos, selfType, blanketPath, blanketTypeParam) and

Copilot uses AI. Check for mistakes.
pos.isSelf()
)
}
Expand Down Expand Up @@ -1083,7 +1084,7 @@
* type `selfType`.
*
* `strippedTypePath` points to the type `strippedType` inside `selfType`,
* which is the (possibly complex-stripped) root type of `selfType`.

Check warning

Code scanning / CodeQL

Missing QLDoc for parameter Warning

The QLDoc has no documentation for m, but the QLDoc mentions selfType
*
* This predicate only checks for matching method names and arities, and whether
* the trait being implemented by `i` (when `i` is not a trait itself) is visible
Expand Down Expand Up @@ -1113,8 +1114,8 @@
}

/**
* Holds if method call `mc` may target a method in blanket implementation `i`
* with `self` parameter having type `selfType`.
* Holds if method call `mc` may target a method in blanket (like) implementation
* `impl` with `self` parameter having type `selfType`.
*
* `blanketPath` points to the type `blanketTypeParam` inside `selfType`, which
* is the type parameter used in the blanket implementation.
Expand All @@ -1125,13 +1126,13 @@
*/
bindingset[mc]
pragma[inline_late]
private predicate methodCallBlanketCandidate(
private predicate methodCallBlanketLikeCandidate(
MethodCall mc, Method m, ImplItemNode impl, AssocFunctionType self, TypePath blanketPath,
TypeParam blanketTypeParam
) {
exists(string name, int arity |
mc.hasNameAndArity(name, arity) and
methodInfoBlanket(m, name, arity, impl, _, self, blanketPath, blanketTypeParam)
methodInfoBlanketLike(m, name, arity, impl, _, self, blanketPath, blanketTypeParam)
|
methodCallVisibleImplTraitCandidate(mc, impl)
or
Expand Down Expand Up @@ -1216,6 +1217,23 @@
borrow), i, _)
}

/**
* Holds if the method inside blanket-like implementation `impl` with matching name
* and arity can be ruled out as a target of this call, either because the candidate
* receiver type represented by `derefChain` and `borrow` is incompatible with the `self`
* parameter type, or because the blanket constraint is not satisfied.
*/
pragma[nomagic]
private predicate hasIncompatibleBlanketLikeTarget(
ImplItemNode impl, string derefChain, boolean borrow
) {
ReceiverIsNotInstantiationOfBlanketLikeSelfParam::argIsNotInstantiationOf(MkMethodCallCand(this,
derefChain, borrow), impl, _)
or
ReceiverSatisfiesBlanketLikeConstraint::satisfiesNotBlanketConstraint(MkMethodCallCand(this,
derefChain, borrow), impl)
}

/**
* Same as `getACandidateReceiverTypeAt`, but with traits substituted in for types
* with trait bounds.
Expand All @@ -1234,18 +1252,41 @@
isComplexRootStripped(strippedTypePath, result)
}

bindingset[strippedTypePath, strippedType, derefChain, borrow]
private predicate hasNoCompatibleTargetCheck(
bindingset[derefChain, borrow, strippedTypePath, strippedType]
private predicate hasNoCompatibleNonBlanketLikeTargetCheck(
string derefChain, boolean borrow, TypePath strippedTypePath, Type strippedType
) {
// todo: also check that all blanket implementation candidates are incompatible
forall(ImplOrTraitItemNode i |
methodCallNonBlanketCandidate(this, _, i, _, strippedTypePath, strippedType)
|
this.hasIncompatibleTarget(i, derefChain, borrow)
)
}

bindingset[derefChain, borrow, strippedTypePath, strippedType]
private predicate hasNoCompatibleTargetCheck(
string derefChain, boolean borrow, TypePath strippedTypePath, Type strippedType
) {
this.hasNoCompatibleNonBlanketLikeTargetCheck(derefChain, borrow, strippedTypePath,
strippedType) and
forall(ImplItemNode i | methodCallBlanketLikeCandidate(this, _, i, _, _, _) |
this.hasIncompatibleBlanketLikeTarget(i, derefChain, borrow)
)
}

bindingset[derefChain, borrow, strippedTypePath, strippedType]
private predicate hasNoCompatibleNonBlanketTargetCheck(
string derefChain, boolean borrow, TypePath strippedTypePath, Type strippedType
) {
this.hasNoCompatibleNonBlanketLikeTargetCheck(derefChain, borrow, strippedTypePath,
strippedType) and
forall(ImplItemNode i |
methodCallBlanketLikeCandidate(this, _, i, _, _, _) and not i.isBlanketImplementation()
|
this.hasIncompatibleBlanketLikeTarget(i, derefChain, borrow)
)
}

/**
* Holds if the candidate receiver type represented by `derefChain` does not
* have a matching method target.
Expand All @@ -1256,7 +1297,7 @@
this.supportsAutoDerefAndBorrow()
or
// needed for the `hasNoCompatibleTarget` check in
// `SatisfiesBlanketConstraintInput::hasBlanketCandidate`
// `ReceiverSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate`
derefChain = ""
) and
exists(TypePath strippedTypePath, Type strippedType |
Expand All @@ -1266,6 +1307,26 @@
)
}

/**
* Holds if the candidate receiver type represented by `derefChain` does not have
* a matching non-blanket method target.
*/
pragma[nomagic]
predicate hasNoCompatibleNonBlanketTargetNoBorrow(string derefChain) {
(
this.supportsAutoDerefAndBorrow()
or
// needed for the `hasNoCompatibleTarget` check in
// `ReceiverSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate`
derefChain = ""
) and
exists(TypePath strippedTypePath, Type strippedType |
not derefChain.matches("%.ref") and // no need to try a borrow if the last thing we did was a deref
strippedType = this.getComplexStrippedType(derefChain, false, strippedTypePath) and
this.hasNoCompatibleNonBlanketTargetCheck(derefChain, false, strippedTypePath, strippedType)
)
}

/**
* Holds if the candidate receiver type represented by `derefChain`, followed
* by a borrow, does not have a matching method target.
Expand All @@ -1275,7 +1336,21 @@
exists(TypePath strippedTypePath, Type strippedType |
this.hasNoCompatibleTargetNoBorrow(derefChain) and
strippedType = this.getComplexStrippedType(derefChain, true, strippedTypePath) and
this.hasNoCompatibleTargetCheck(derefChain, true, strippedTypePath, strippedType)
this.hasNoCompatibleNonBlanketLikeTargetCheck(derefChain, true, strippedTypePath,
strippedType)
)
}

/**
* Holds if the candidate receiver type represented by `derefChain`, followed
* by a borrow, does not have a matching non-blanket method target.
*/
pragma[nomagic]
predicate hasNoCompatibleNonBlanketTargetBorrow(string derefChain) {
exists(TypePath strippedTypePath, Type strippedType |
this.hasNoCompatibleTargetNoBorrow(derefChain) and
strippedType = this.getComplexStrippedType(derefChain, true, strippedTypePath) and
this.hasNoCompatibleNonBlanketTargetCheck(derefChain, true, strippedTypePath, strippedType)
)
}

Expand Down Expand Up @@ -1470,11 +1545,11 @@
}

pragma[nomagic]
predicate hasNoCompatibleTarget() {
mc_.hasNoCompatibleTargetBorrow(derefChain) and
predicate hasNoCompatibleNonBlanketTarget() {
mc_.hasNoCompatibleNonBlanketTargetBorrow(derefChain) and
borrow = true
or
mc_.hasNoCompatibleTargetNoBorrow(derefChain) and
mc_.hasNoCompatibleNonBlanketTargetNoBorrow(derefChain) and
borrow = false
}

Expand Down Expand Up @@ -1555,20 +1630,20 @@
Location getLocation() { result = mc_.getLocation() }
}

private module ReceiverSatisfiesBlanketConstraintInput implements
private module ReceiverSatisfiesBlanketLikeConstraintInput implements
BlanketImplementation::SatisfiesBlanketConstraintInputSig<MethodCallCand>
{
pragma[nomagic]
predicate hasBlanketCandidate(
MethodCallCand mcc, ImplItemNode impl, TypePath blanketPath, TypeParam blanketTypeParam
) {
exists(MethodCall mc, string name, int arity |
mcc.hasSignature(mc, _, _, name, arity) and
methodCallBlanketCandidate(mc, _, impl, _, blanketPath, blanketTypeParam) and
exists(MethodCall mc |
mc = mcc.getMethodCall() and
methodCallBlanketLikeCandidate(mc, _, impl, _, blanketPath, blanketTypeParam) and
// Only apply blanket implementations when no other implementations are possible;
// this is to account for codebases that use the (unstable) specialization feature
// (https://rust-lang.github.io/rfcs/1210-impl-specialization.html)
mcc.hasNoCompatibleTarget()
(mcc.hasNoCompatibleNonBlanketTarget() or not impl.isBlanketImplementation())
|
mcc.hasNoBorrow()
or
Expand All @@ -1577,9 +1652,9 @@
}
}

private module ReceiverSatisfiesBlanketConstraint =
private module ReceiverSatisfiesBlanketLikeConstraint =
BlanketImplementation::SatisfiesBlanketConstraint<MethodCallCand,
ReceiverSatisfiesBlanketConstraintInput>;
ReceiverSatisfiesBlanketLikeConstraintInput>;

/**
* A configuration for matching the type of a receiver against the type of
Expand All @@ -1600,8 +1675,8 @@
|
methodCallNonBlanketCandidate(mc, m, i, selfType, strippedTypePath, strippedType)
or
methodCallBlanketCandidate(mc, m, i, selfType, _, _) and
ReceiverSatisfiesBlanketConstraint::satisfiesBlanketConstraint(mcc, i)
methodCallBlanketLikeCandidate(mc, m, i, selfType, _, _) and
ReceiverSatisfiesBlanketLikeConstraint::satisfiesBlanketConstraint(mcc, i)
)
}

Expand All @@ -1626,6 +1701,30 @@
private module ReceiverIsInstantiationOfSelfParam =
ArgIsInstantiationOf<MethodCallCand, ReceiverIsInstantiationOfSelfParamInput>;

/**
* A configuration for anti-matching the type of a receiver against the type of
* a `self` parameter belonging to a blanket (like) implementation.
*/
private module ReceiverIsNotInstantiationOfBlanketLikeSelfParamInput implements
IsInstantiationOfInputSig<MethodCallCand, AssocFunctionType>
{
pragma[nomagic]
predicate potentialInstantiationOf(
MethodCallCand mcc, TypeAbstraction abs, AssocFunctionType constraint
) {
methodCallBlanketLikeCandidate(mcc.getMethodCall(), _, abs, constraint, _, _) and
if abs.(Impl).hasTrait()
then
// inherent methods take precedence over trait methods, so only allow
// trait methods when there are no matching inherent methods
mcc.hasNoInherentTarget()
else any()
}
}

private module ReceiverIsNotInstantiationOfBlanketLikeSelfParam =
ArgIsInstantiationOf<MethodCallCand, ReceiverIsNotInstantiationOfBlanketLikeSelfParamInput>;

/**
* A configuration for matching the type qualifier of a method call
* against the type being implemented in an `impl` block. For example,
Expand Down Expand Up @@ -1679,10 +1778,6 @@
ReceiverIsInstantiationOfSelfParamInput::potentialInstantiationOf0(mcc, abs, constraint) and
abs = any(Impl i | not i.hasTrait())
}

predicate relevantConstraint(AssocFunctionType constraint) {
methodInfo(_, _, _, _, constraint, _, _)
}
}

private module ReceiverIsNotInstantiationOfInherentSelfParam =
Expand Down Expand Up @@ -1945,40 +2040,40 @@
)
else any()
)
}

Check warning

Code scanning / CodeQL

Omittable 'exists' variable Warning

This exists variable can be omitted by using a don't-care expression
in this argument
.

pragma[nomagic]
private predicate functionInfoBlanketRelevantPos(
private predicate functionInfoBlanketLikeRelevantPos(
NonMethodFunction f, string name, int arity, ImplItemNode impl, Trait trait,
FunctionPosition pos, AssocFunctionType t, TypePath blanketPath, TypeParam blanketTypeParam
) {
functionInfoBlanket(f, name, arity, impl, trait, pos, t, blanketPath, blanketTypeParam) and
functionInfoBlanketLike(f, name, arity, impl, trait, pos, t, blanketPath, blanketTypeParam) and
(
if pos.isReturn()
then
// We only check that the context of the call provides relevant type information
// when no argument can
not exists(FunctionPosition pos0 |
functionInfoBlanket(f, name, arity, impl, trait, pos0, _, _, _) and
functionInfoBlanketLike(f, name, arity, impl, trait, pos0, _, _, _) and
not pos0.isReturn()
)
else any()
)
}

pragma[nomagic]
private predicate blanketCallTraitCandidate(Element fc, Trait trait) {
private predicate blanketLikeCallTraitCandidate(Element fc, Trait trait) {
exists(string name, int arity |
fc.(NonMethodCall).hasNameAndArity(name, arity) and
functionInfoBlanketRelevantPos(_, name, arity, _, trait, _, _, _, _)
functionInfoBlanketLikeRelevantPos(_, name, arity, _, trait, _, _, _, _)
|
not fc.(Call).hasTrait()
or
trait = fc.(Call).getTrait()
)
}

private module BlanketTraitIsVisible = TraitIsVisible<blanketCallTraitCandidate/2>;
private module BlanketTraitIsVisible = TraitIsVisible<blanketLikeCallTraitCandidate/2>;

/** A (potential) non-method call, `f(x)`. */
final class NonMethodCall extends CallExpr {
Expand Down Expand Up @@ -2037,13 +2132,13 @@
}

pragma[nomagic]
predicate resolveCallTargetBlanketCandidate(
predicate resolveCallTargetBlanketLikeCandidate(
ImplItemNode impl, FunctionPosition pos, TypePath blanketPath, TypeParam blanketTypeParam
) {
exists(string name, int arity, Trait trait, AssocFunctionType t |
this.hasNameAndArity(name, arity) and
exists(this.getTypeAt(pos, blanketPath)) and
functionInfoBlanketRelevantPos(_, name, arity, impl, trait, pos, t, blanketPath,
functionInfoBlanketLikeRelevantPos(_, name, arity, impl, trait, pos, t, blanketPath,
blanketTypeParam) and
BlanketTraitIsVisible::traitIsVisible(this, trait)
)
Expand Down Expand Up @@ -2080,7 +2175,7 @@

private newtype TCallAndBlanketPos =
MkCallAndBlanketPos(NonMethodCall fc, FunctionPosition pos) {
fc.resolveCallTargetBlanketCandidate(_, pos, _, _)
fc.resolveCallTargetBlanketLikeCandidate(_, pos, _, _)
}

/** A call tagged with a position. */
Expand All @@ -2106,7 +2201,7 @@
) {
exists(NonMethodCall fc, FunctionPosition pos |
fcp = MkCallAndBlanketPos(fc, pos) and
fc.resolveCallTargetBlanketCandidate(impl, pos, blanketPath, blanketTypeParam)
fc.resolveCallTargetBlanketLikeCandidate(impl, pos, blanketPath, blanketTypeParam)
)
}
}
Expand All @@ -2129,12 +2224,12 @@
exists(FunctionPosition pos |
ArgSatisfiesBlanketConstraint::satisfiesBlanketConstraint(fcp, abs) and
fcp = MkCallAndBlanketPos(_, pos) and
functionInfoBlanketRelevantPos(_, _, _, abs, _, pos, constraint, _, _)
functionInfoBlanketLikeRelevantPos(_, _, _, abs, _, pos, constraint, _, _)
)
}

predicate relevantConstraint(AssocFunctionType constraint) {
functionInfoBlanketRelevantPos(_, _, _, _, _, _, constraint, _, _)
functionInfoBlanketLikeRelevantPos(_, _, _, _, _, _, constraint, _, _)
}
}

Expand Down
Loading
Loading