Skip to content

Commit c77c3ec

Browse files
committed
Rust: Remove restriction that blanket(-like) impls must have a constraint
1 parent a5d9cb1 commit c77c3ec

File tree

7 files changed

+179
-80
lines changed

7 files changed

+179
-80
lines changed

rust/ql/lib/codeql/rust/internal/typeinference/BlanketImplementation.qll

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ module SatisfiesBlanketConstraint<
126126

127127
/**
128128
* Holds if the argument type `at` satisfies the first non-trivial blanket
129-
* constraint of `impl`.
129+
* constraint of `impl`, or if there are no non-trivial constraints of `impl`.
130130
*/
131131
pragma[nomagic]
132132
predicate satisfiesBlanketConstraint(ArgumentType at, ImplItemNode impl) {
@@ -135,6 +135,11 @@ module SatisfiesBlanketConstraint<
135135
SatisfiesBlanketConstraintInput::relevantConstraint(ato, impl, traitBound) and
136136
SatisfiesBlanketConstraint::satisfiesConstraintType(ato, TTrait(traitBound), _, _)
137137
)
138+
or
139+
exists(TypeParam blanketTypeParam |
140+
hasBlanketCandidate(at, impl, _, blanketTypeParam) and
141+
not hasFirstNonTrivialTraitBound(blanketTypeParam, _)
142+
)
138143
}
139144

140145
/**

rust/ql/lib/codeql/rust/internal/typeinference/FunctionOverloading.qll

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,26 @@ private predicate implSiblings(TraitItemNode trait, Impl impl1, Impl impl2) {
5757
)
5858
}
5959

60+
pragma[nomagic]
61+
private predicate isBlanketImpl(ImplItemNode impl, Trait trait) {
62+
impl.isBlanketImplementation() and
63+
trait = impl.resolveTraitTy()
64+
}
65+
6066
/**
6167
* Holds if `impl` is an implementation of `trait` and if another implementation
6268
* exists for the same type.
6369
*/
6470
pragma[nomagic]
65-
private predicate implHasSibling(Impl impl, Trait trait) { implSiblings(trait, impl, _) }
71+
private predicate implHasSibling(ImplItemNode impl, Trait trait) {
72+
implSiblings(trait, impl, _)
73+
or
74+
exists(ImplItemNode other |
75+
isBlanketImpl(impl, trait) and
76+
isBlanketImpl(other, trait) and
77+
impl != other
78+
)
79+
}
6680

6781
/**
6882
* Holds if type parameter `tp` of `trait` occurs in the function `f` with the name

rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ module ArgsAreInstantiationsOf<ArgsAreInstantiationsOfInputSig Input> {
355355
string toString() { result = call.toString() + " [arg " + pos + "]" }
356356
}
357357

358-
private module ArgIsInstantiationOfInput implements
358+
private module ArgIsInstantiationOfToIndexInput implements
359359
IsInstantiationOfInputSig<CallAndPos, AssocFunctionType>
360360
{
361361
pragma[nomagic]
@@ -388,7 +388,7 @@ module ArgsAreInstantiationsOf<ArgsAreInstantiationsOfInputSig Input> {
388388
}
389389

390390
private module ArgIsInstantiationOfToIndex =
391-
ArgIsInstantiationOf<CallAndPos, ArgIsInstantiationOfInput>;
391+
ArgIsInstantiationOf<CallAndPos, ArgIsInstantiationOfToIndexInput>;
392392

393393
pragma[nomagic]
394394
private predicate argsAreInstantiationsOfToIndex(
@@ -412,4 +412,24 @@ module ArgsAreInstantiationsOf<ArgsAreInstantiationsOfInputSig Input> {
412412
rnk = max(int r | toCheckRanked(i, f, _, r))
413413
)
414414
}
415+
416+
pragma[nomagic]
417+
private predicate argsAreNotInstantiationsOf0(
418+
Input::Call call, FunctionPosition pos, ImplOrTraitItemNode i
419+
) {
420+
ArgIsInstantiationOfToIndex::argIsNotInstantiationOf(MkCallAndPos(call, pos), i, _, _)
421+
}
422+
423+
/**
424+
* Holds if _some_ argument of `call` has a type that is not an instantiation of the
425+
* type of the corresponding parameter of `f` inside `i`.
426+
*/
427+
pragma[nomagic]
428+
predicate argsAreNotInstantiationsOf(Input::Call call, ImplOrTraitItemNode i, Function f) {
429+
exists(FunctionPosition pos |
430+
argsAreNotInstantiationsOf0(call, pos, i) and
431+
call.hasTargetCand(i, f) and
432+
Input::toCheck(i, f, pos, _)
433+
)
434+
}
415435
}

rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll

Lines changed: 71 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2744,7 +2744,7 @@ private module NonMethodResolution {
27442744
* Gets the blanket function that this call may resolve to, if any.
27452745
*/
27462746
pragma[nomagic]
2747-
private NonMethodFunction resolveCallTargetBlanketCand(ImplItemNode impl) {
2747+
NonMethodFunction resolveCallTargetBlanketCand(ImplItemNode impl) {
27482748
exists(string name |
27492749
this.hasNameAndArity(pragma[only_bind_into](name), _) and
27502750
ArgIsInstantiationOfBlanketParam::argIsInstantiationOf(MkCallAndBlanketPos(this, _), impl, _) and
@@ -2759,12 +2759,11 @@ private module NonMethodResolution {
27592759
predicate hasTrait() { exists(this.getTrait()) }
27602760

27612761
pragma[nomagic]
2762-
NonMethodFunction resolveAssocCallTargetCand(ImplItemNode i) {
2762+
NonMethodFunction resolveCallTargetNonBlanketCand(ImplItemNode i) {
27632763
not this.hasTrait() and
27642764
result = this.getPathResolutionResolved() and
2765-
result = i.getASuccessor(_)
2766-
or
2767-
result = this.resolveCallTargetBlanketCand(i)
2765+
result = i.getASuccessor(_) and
2766+
FunctionOverloading::functionResolutionDependsOnArgument(_, result, _, _, _)
27682767
}
27692768

27702769
AstNode getNodeAt(FunctionPosition pos) {
@@ -2796,6 +2795,16 @@ private module NonMethodResolution {
27962795
trait = this.getTrait()
27972796
}
27982797

2798+
pragma[nomagic]
2799+
predicate hasNoCompatibleNonBlanketTarget() {
2800+
not exists(this.resolveCallTargetViaPathResolution()) and
2801+
forall(ImplOrTraitItemNode i, Function f |
2802+
this.(NonMethodArgsAreInstantiationsOfNonBlanketInput::Call).hasTargetCand(i, f)
2803+
|
2804+
NonMethodArgsAreInstantiationsOfNonBlanket::argsAreNotInstantiationsOf(this, i, f)
2805+
)
2806+
}
2807+
27992808
/**
28002809
* Gets the target of this call, which can be resolved using only path resolution.
28012810
*/
@@ -2814,7 +2823,9 @@ private module NonMethodResolution {
28142823
result = this.resolveCallTargetBlanketCand(i) and
28152824
not FunctionOverloading::functionResolutionDependsOnArgument(_, result, _, _, _)
28162825
or
2817-
NonMethodArgsAreInstantiationsOf::argsAreInstantiationsOf(this, i, result)
2826+
NonMethodArgsAreInstantiationsOfBlanket::argsAreInstantiationsOf(this, i, result)
2827+
or
2828+
NonMethodArgsAreInstantiationsOfNonBlanket::argsAreInstantiationsOf(this, i, result)
28182829
}
28192830

28202831
pragma[nomagic]
@@ -2853,7 +2864,11 @@ private module NonMethodResolution {
28532864
) {
28542865
exists(NonMethodCall fc, FunctionPosition pos |
28552866
fcp = MkCallAndBlanketPos(fc, pos) and
2856-
fc.resolveCallTargetBlanketLikeCandidate(impl, pos, blanketPath, blanketTypeParam)
2867+
fc.resolveCallTargetBlanketLikeCandidate(impl, pos, blanketPath, blanketTypeParam) and
2868+
// Only apply blanket implementations when no other implementations are possible;
2869+
// this is to account for codebases that use the (unstable) specialization feature
2870+
// (https://rust-lang.github.io/rfcs/1210-impl-specialization.html)
2871+
(fc.hasNoCompatibleNonBlanketTarget() or not impl.isBlanketImplementation())
28572872
)
28582873
}
28592874
}
@@ -2888,37 +2903,29 @@ private module NonMethodResolution {
28882903
private module ArgIsInstantiationOfBlanketParam =
28892904
ArgIsInstantiationOf<CallAndBlanketPos, ArgIsInstantiationOfBlanketParamInput>;
28902905

2891-
private module NonMethodArgsAreInstantiationsOfInput implements ArgsAreInstantiationsOfInputSig {
2906+
private module NonMethodArgsAreInstantiationsOfBlanketInput implements
2907+
ArgsAreInstantiationsOfInputSig
2908+
{
28922909
predicate toCheck(ImplOrTraitItemNode i, Function f, FunctionPosition pos, AssocFunctionType t) {
28932910
t.appliesTo(f, i, pos) and
2894-
(
2895-
exists(Type t0 |
2896-
// for now, we do not handle ambiguous targets when one of the types it iself
2897-
// a type parameter; we should be checking the constraints on that type parameter
2898-
// in this case
2899-
not t0 instanceof TypeParameter
2900-
|
2901-
FunctionOverloading::functionResolutionDependsOnArgument(i, f, pos, _, t0)
2902-
or
2903-
traitFunctionDependsOnPos(_, _, pos, t0, i, f)
2904-
)
2911+
exists(Type t0 |
2912+
// for now, we do not handle ambiguous targets when one of the types it iself
2913+
// a type parameter; we should be checking the constraints on that type parameter
2914+
// in this case
2915+
not t0 instanceof TypeParameter
2916+
|
2917+
FunctionOverloading::functionResolutionDependsOnArgument(i, f, pos, _, t0)
29052918
or
2906-
// match against the trait function itself
2907-
exists(Trait trait |
2908-
FunctionOverloading::traitTypeParameterOccurrence(trait, f, _, pos, _,
2909-
TSelfTypeParameter(trait))
2910-
)
2919+
traitFunctionDependsOnPos(_, _, pos, t0, i, f)
29112920
)
29122921
}
29132922

2914-
class Call extends NonMethodCall {
2923+
final class Call extends NonMethodCall {
29152924
Type getArgType(FunctionPosition pos, TypePath path) {
29162925
result = inferType(this.getNodeAt(pos), path)
29172926
}
29182927

2919-
predicate hasTargetCand(ImplOrTraitItemNode i, Function f) {
2920-
f = this.resolveAssocCallTargetCand(i)
2921-
or
2928+
predicate hasTraitResolvedCand(ImplOrTraitItemNode i, Function f) {
29222929
exists(TraitItemNode trait, NonMethodFunction resolved, ImplItemNode i1, Function f1 |
29232930
this.hasTraitResolved(trait, resolved) and
29242931
traitFunctionDependsOnPos(trait, resolved, _, _, i1, f1)
@@ -2930,11 +2937,45 @@ private module NonMethodResolution {
29302937
i = trait
29312938
)
29322939
}
2940+
2941+
predicate hasTargetCand(ImplOrTraitItemNode i, Function f) {
2942+
f = this.resolveCallTargetBlanketCand(i)
2943+
or
2944+
this.hasTraitResolvedCand(i, f) and
2945+
BlanketImplementation::isBlanketLike(i, _, _)
2946+
}
2947+
}
2948+
}
2949+
2950+
private module NonMethodArgsAreInstantiationsOfBlanket =
2951+
ArgsAreInstantiationsOf<NonMethodArgsAreInstantiationsOfBlanketInput>;
2952+
2953+
private module NonMethodArgsAreInstantiationsOfNonBlanketInput implements
2954+
ArgsAreInstantiationsOfInputSig
2955+
{
2956+
predicate toCheck(ImplOrTraitItemNode i, Function f, FunctionPosition pos, AssocFunctionType t) {
2957+
NonMethodArgsAreInstantiationsOfBlanketInput::toCheck(i, f, pos, t)
2958+
or
2959+
// match against the trait function itself
2960+
t.appliesTo(f, i, pos) and
2961+
exists(Trait trait |
2962+
FunctionOverloading::traitTypeParameterOccurrence(trait, f, _, pos, _,
2963+
TSelfTypeParameter(trait))
2964+
)
2965+
}
2966+
2967+
class Call extends NonMethodArgsAreInstantiationsOfBlanketInput::Call {
2968+
predicate hasTargetCand(ImplOrTraitItemNode i, Function f) {
2969+
f = this.resolveCallTargetNonBlanketCand(i)
2970+
or
2971+
this.hasTraitResolvedCand(i, f) and
2972+
not BlanketImplementation::isBlanketLike(i, _, _)
2973+
}
29332974
}
29342975
}
29352976

2936-
private module NonMethodArgsAreInstantiationsOf =
2937-
ArgsAreInstantiationsOf<NonMethodArgsAreInstantiationsOfInput>;
2977+
private module NonMethodArgsAreInstantiationsOfNonBlanket =
2978+
ArgsAreInstantiationsOf<NonMethodArgsAreInstantiationsOfNonBlanketInput>;
29382979
}
29392980

29402981
abstract private class TupleLikeConstructor extends Addressable {

0 commit comments

Comments
 (0)