Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ module SatisfiesBlanketConstraint<

/**
* Holds if the argument type `at` satisfies the first non-trivial blanket
* constraint of `impl`.
* constraint of `impl`, or if there are no non-trivial constraints of `impl`.
*/
pragma[nomagic]
predicate satisfiesBlanketConstraint(ArgumentType at, ImplItemNode impl) {
Expand All @@ -135,6 +135,11 @@ module SatisfiesBlanketConstraint<
SatisfiesBlanketConstraintInput::relevantConstraint(ato, impl, traitBound) and
SatisfiesBlanketConstraint::satisfiesConstraintType(ato, TTrait(traitBound), _, _)
)
or
exists(TypeParam blanketTypeParam |
hasBlanketCandidate(at, impl, _, blanketTypeParam) and
not hasFirstNonTrivialTraitBound(blanketTypeParam, _)
)
}

/**
Expand Down
24 changes: 22 additions & 2 deletions rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ module ArgsAreInstantiationsOf<ArgsAreInstantiationsOfInputSig Input> {
string toString() { result = call.toString() + " [arg " + pos + "]" }
}

private module ArgIsInstantiationOfInput implements
private module ArgIsInstantiationOfToIndexInput implements
IsInstantiationOfInputSig<CallAndPos, AssocFunctionType>
{
pragma[nomagic]
Expand Down Expand Up @@ -388,7 +388,7 @@ module ArgsAreInstantiationsOf<ArgsAreInstantiationsOfInputSig Input> {
}

private module ArgIsInstantiationOfToIndex =
ArgIsInstantiationOf<CallAndPos, ArgIsInstantiationOfInput>;
ArgIsInstantiationOf<CallAndPos, ArgIsInstantiationOfToIndexInput>;

pragma[nomagic]
private predicate argsAreInstantiationsOfToIndex(
Expand All @@ -412,4 +412,24 @@ module ArgsAreInstantiationsOf<ArgsAreInstantiationsOfInputSig Input> {
rnk = max(int r | toCheckRanked(i, f, _, r))
)
}

pragma[nomagic]
private predicate argsAreNotInstantiationsOf0(
Input::Call call, FunctionPosition pos, ImplOrTraitItemNode i
) {
ArgIsInstantiationOfToIndex::argIsNotInstantiationOf(MkCallAndPos(call, pos), i, _, _)
}

/**
* Holds if _some_ argument of `call` has a type that is not an instantiation of the
* type of the corresponding parameter of `f` inside `i`.
*/
pragma[nomagic]
predicate argsAreNotInstantiationsOf(Input::Call call, ImplOrTraitItemNode i, Function f) {
exists(FunctionPosition pos |
argsAreNotInstantiationsOf0(call, pos, i) and
call.hasTargetCand(i, f) and
Input::toCheck(i, f, pos, _)
)
}
}
101 changes: 71 additions & 30 deletions rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll
Original file line number Diff line number Diff line change
Expand Up @@ -2741,7 +2741,7 @@ private module NonMethodResolution {
* Gets the blanket function that this call may resolve to, if any.
*/
pragma[nomagic]
private NonMethodFunction resolveCallTargetBlanketCand(ImplItemNode impl) {
NonMethodFunction resolveCallTargetBlanketCand(ImplItemNode impl) {
exists(string name |
this.hasNameAndArity(pragma[only_bind_into](name), _) and
ArgIsInstantiationOfBlanketParam::argIsInstantiationOf(MkCallAndBlanketPos(this, _), impl, _) and
Expand All @@ -2756,12 +2756,11 @@ private module NonMethodResolution {
predicate hasTrait() { exists(this.getTrait()) }

pragma[nomagic]
NonMethodFunction resolveAssocCallTargetCand(ImplItemNode i) {
NonMethodFunction resolveCallTargetNonBlanketCand(ImplItemNode i) {
not this.hasTrait() and
result = this.getPathResolutionResolved() and
result = i.getASuccessor(_)
or
result = this.resolveCallTargetBlanketCand(i)
result = i.getASuccessor(_) and
FunctionOverloading::functionResolutionDependsOnArgument(_, result, _, _, _)
}

AstNode getNodeAt(FunctionPosition pos) {
Expand Down Expand Up @@ -2793,6 +2792,16 @@ private module NonMethodResolution {
trait = this.getTrait()
}

pragma[nomagic]
predicate hasNoCompatibleNonBlanketTarget() {
not exists(this.resolveCallTargetViaPathResolution()) and
forall(ImplOrTraitItemNode i, Function f |
this.(NonMethodArgsAreInstantiationsOfNonBlanketInput::Call).hasTargetCand(i, f)
|
NonMethodArgsAreInstantiationsOfNonBlanket::argsAreNotInstantiationsOf(this, i, f)
)
}

/**
* Gets the target of this call, which can be resolved using only path resolution.
*/
Expand All @@ -2811,7 +2820,9 @@ private module NonMethodResolution {
result = this.resolveCallTargetBlanketCand(i) and
not FunctionOverloading::functionResolutionDependsOnArgument(_, result, _, _, _)
or
NonMethodArgsAreInstantiationsOf::argsAreInstantiationsOf(this, i, result)
NonMethodArgsAreInstantiationsOfBlanket::argsAreInstantiationsOf(this, i, result)
or
NonMethodArgsAreInstantiationsOfNonBlanket::argsAreInstantiationsOf(this, i, result)
}

pragma[nomagic]
Expand Down Expand Up @@ -2850,7 +2861,11 @@ private module NonMethodResolution {
) {
exists(NonMethodCall fc, FunctionPosition pos |
fcp = MkCallAndBlanketPos(fc, pos) and
fc.resolveCallTargetBlanketLikeCandidate(impl, pos, blanketPath, blanketTypeParam)
fc.resolveCallTargetBlanketLikeCandidate(impl, pos, 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)
(fc.hasNoCompatibleNonBlanketTarget() or not impl.isBlanketImplementation())
)
}
}
Expand Down Expand Up @@ -2885,37 +2900,29 @@ private module NonMethodResolution {
private module ArgIsInstantiationOfBlanketParam =
ArgIsInstantiationOf<CallAndBlanketPos, ArgIsInstantiationOfBlanketParamInput>;

private module NonMethodArgsAreInstantiationsOfInput implements ArgsAreInstantiationsOfInputSig {
private module NonMethodArgsAreInstantiationsOfBlanketInput implements
ArgsAreInstantiationsOfInputSig
{
predicate toCheck(ImplOrTraitItemNode i, Function f, FunctionPosition pos, AssocFunctionType t) {
t.appliesTo(f, i, pos) and
(
exists(Type t0 |
// for now, we do not handle ambiguous targets when one of the types it iself
// a type parameter; we should be checking the constraints on that type parameter
// in this case
not t0 instanceof TypeParameter
|
FunctionOverloading::functionResolutionDependsOnArgument(i, f, pos, _, t0)
or
traitFunctionDependsOnPos(_, _, pos, t0, i, f)
)
exists(Type t0 |
// for now, we do not handle ambiguous targets when one of the types it iself
// a type parameter; we should be checking the constraints on that type parameter
// in this case
not t0 instanceof TypeParameter
|
FunctionOverloading::functionResolutionDependsOnArgument(i, f, pos, _, t0)
or
// match against the trait function itself
exists(Trait trait |
FunctionOverloading::traitTypeParameterOccurrence(trait, f, _, pos, _,
TSelfTypeParameter(trait))
)
traitFunctionDependsOnPos(_, _, pos, t0, i, f)
)
}

class Call extends NonMethodCall {
final class Call extends NonMethodCall {
Type getArgType(FunctionPosition pos, TypePath path) {
result = inferType(this.getNodeAt(pos), path)
}

predicate hasTargetCand(ImplOrTraitItemNode i, Function f) {
f = this.resolveAssocCallTargetCand(i)
or
predicate hasTraitResolvedCand(ImplOrTraitItemNode i, Function f) {
exists(TraitItemNode trait, NonMethodFunction resolved, ImplItemNode i1, Function f1 |
this.hasTraitResolved(trait, resolved) and
traitFunctionDependsOnPos(trait, resolved, _, _, i1, f1)
Expand All @@ -2927,11 +2934,45 @@ private module NonMethodResolution {
i = trait
)
}

predicate hasTargetCand(ImplOrTraitItemNode i, Function f) {
f = this.resolveCallTargetBlanketCand(i)
or
this.hasTraitResolvedCand(i, f) and
BlanketImplementation::isBlanketLike(i, _, _)
}
}
}

private module NonMethodArgsAreInstantiationsOfBlanket =
ArgsAreInstantiationsOf<NonMethodArgsAreInstantiationsOfBlanketInput>;

private module NonMethodArgsAreInstantiationsOfNonBlanketInput implements
ArgsAreInstantiationsOfInputSig
{
predicate toCheck(ImplOrTraitItemNode i, Function f, FunctionPosition pos, AssocFunctionType t) {
NonMethodArgsAreInstantiationsOfBlanketInput::toCheck(i, f, pos, t)
or
// match against the trait function itself
t.appliesTo(f, i, pos) and
exists(Trait trait |
FunctionOverloading::traitTypeParameterOccurrence(trait, f, _, pos, _,
TSelfTypeParameter(trait))
)
}

class Call extends NonMethodArgsAreInstantiationsOfBlanketInput::Call {
predicate hasTargetCand(ImplOrTraitItemNode i, Function f) {
f = this.resolveCallTargetNonBlanketCand(i)
or
this.hasTraitResolvedCand(i, f) and
not BlanketImplementation::isBlanketLike(i, _, _)
}
}
}

private module NonMethodArgsAreInstantiationsOf =
ArgsAreInstantiationsOf<NonMethodArgsAreInstantiationsOfInput>;
private module NonMethodArgsAreInstantiationsOfNonBlanket =
ArgsAreInstantiationsOf<NonMethodArgsAreInstantiationsOfNonBlanketInput>;
}

abstract private class TupleLikeConstructor extends Addressable {
Expand Down
Loading