Skip to content

Commit c0f1462

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

File tree

8 files changed

+215
-84
lines changed

8 files changed

+215
-84
lines changed

rust/ql/lib/codeql/rust/frameworks/stdlib/Stdlib.qll

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
private import rust
66
private import codeql.rust.Concepts
77
private import codeql.rust.dataflow.DataFlow
8+
private import codeql.rust.dataflow.FlowSummary
89
private import codeql.rust.internal.PathResolution
10+
private import codeql.rust.internal.typeinference.Type
11+
private import codeql.rust.internal.typeinference.TypeMention
912

1013
/**
1114
* A call to the `starts_with` method on a `Path`.
@@ -297,3 +300,28 @@ class Vec extends Struct {
297300
/** Gets the type parameter representing the element type. */
298301
TypeParam getElementTypeParam() { result = this.getGenericParamList().getTypeParam(0) }
299302
}
303+
304+
private class ReflexiveFrom extends SummarizedCallable::Range {
305+
ReflexiveFrom() {
306+
exists(ImplItemNode impl |
307+
impl.resolveTraitTy().(Trait).getCanonicalPath() = "core::convert::From" and
308+
this = impl.getAnAssocItem() and
309+
impl.isBlanketImplementation() and
310+
this.getParam(0)
311+
.getTypeRepr()
312+
.(TypeMention)
313+
.resolveType()
314+
.(TypeParamTypeParameter)
315+
.getTypeParam() = impl.getTypeParam(0)
316+
)
317+
}
318+
319+
override predicate propagatesFlow(
320+
string input, string output, boolean preservesValue, string model
321+
) {
322+
input = "Argument[0]" and
323+
output = "ReturnValue" and
324+
preservesValue = true and
325+
model = "ReflexiveFrom"
326+
}
327+
}

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: 79 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,6 +1287,13 @@ private class BorrowKind extends TBorrowKind {
12871287
}
12881288
}
12891289

1290+
// for now, we do not handle ambiguous targets when one of the types it iself
1291+
// a constrained type parameter; we should be checking the constraints in this case
1292+
private predicate typeCanBeUsedForDisambiguation(Type t) {
1293+
not t instanceof TypeParameter or
1294+
t.(TypeParamTypeParameter).getTypeParam() = any(TypeParam tp | not exists(tp.getATypeBound()))
1295+
}
1296+
12901297
/**
12911298
* Provides logic for resolving calls to methods.
12921299
*
@@ -2382,10 +2389,7 @@ private module MethodResolution {
23822389
exists(TypePath path, Type t0 |
23832390
FunctionOverloading::functionResolutionDependsOnArgument(i, f, pos, path, t0) and
23842391
t.appliesTo(f, i, pos) and
2385-
// for now, we do not handle ambiguous targets when one of the types it iself
2386-
// a type parameter; we should be checking the constraints on that type parameter
2387-
// in this case
2388-
not t0 instanceof TypeParameter
2392+
typeCanBeUsedForDisambiguation(t0)
23892393
)
23902394
}
23912395

@@ -2744,7 +2748,7 @@ private module NonMethodResolution {
27442748
* Gets the blanket function that this call may resolve to, if any.
27452749
*/
27462750
pragma[nomagic]
2747-
private NonMethodFunction resolveCallTargetBlanketCand(ImplItemNode impl) {
2751+
NonMethodFunction resolveCallTargetBlanketCand(ImplItemNode impl) {
27482752
exists(string name |
27492753
this.hasNameAndArity(pragma[only_bind_into](name), _) and
27502754
ArgIsInstantiationOfBlanketParam::argIsInstantiationOf(MkCallAndBlanketPos(this, _), impl, _) and
@@ -2759,12 +2763,11 @@ private module NonMethodResolution {
27592763
predicate hasTrait() { exists(this.getTrait()) }
27602764

27612765
pragma[nomagic]
2762-
NonMethodFunction resolveAssocCallTargetCand(ImplItemNode i) {
2766+
NonMethodFunction resolveCallTargetNonBlanketCand(ImplItemNode i) {
27632767
not this.hasTrait() and
27642768
result = this.getPathResolutionResolved() and
2765-
result = i.getASuccessor(_)
2766-
or
2767-
result = this.resolveCallTargetBlanketCand(i)
2769+
result = i.getASuccessor(_) and
2770+
FunctionOverloading::functionResolutionDependsOnArgument(_, result, _, _, _)
27682771
}
27692772

27702773
AstNode getNodeAt(FunctionPosition pos) {
@@ -2796,6 +2799,21 @@ private module NonMethodResolution {
27962799
trait = this.getTrait()
27972800
}
27982801

2802+
/**
2803+
* Holds if this call has no compatible non-blanket target, and it has some
2804+
* candidate blanket target.
2805+
*/
2806+
pragma[nomagic]
2807+
predicate hasNoCompatibleNonBlanketTarget() {
2808+
this.resolveCallTargetBlanketLikeCandidate(_, _, _, _) and
2809+
not exists(this.resolveCallTargetViaPathResolution()) and
2810+
forall(ImplOrTraitItemNode i, Function f |
2811+
this.(NonMethodArgsAreInstantiationsOfNonBlanketInput::Call).hasTargetCand(i, f)
2812+
|
2813+
NonMethodArgsAreInstantiationsOfNonBlanket::argsAreNotInstantiationsOf(this, i, f)
2814+
)
2815+
}
2816+
27992817
/**
28002818
* Gets the target of this call, which can be resolved using only path resolution.
28012819
*/
@@ -2814,7 +2832,9 @@ private module NonMethodResolution {
28142832
result = this.resolveCallTargetBlanketCand(i) and
28152833
not FunctionOverloading::functionResolutionDependsOnArgument(_, result, _, _, _)
28162834
or
2817-
NonMethodArgsAreInstantiationsOf::argsAreInstantiationsOf(this, i, result)
2835+
NonMethodArgsAreInstantiationsOfBlanket::argsAreInstantiationsOf(this, i, result)
2836+
or
2837+
NonMethodArgsAreInstantiationsOfNonBlanket::argsAreInstantiationsOf(this, i, result)
28182838
}
28192839

28202840
pragma[nomagic]
@@ -2853,7 +2873,11 @@ private module NonMethodResolution {
28532873
) {
28542874
exists(NonMethodCall fc, FunctionPosition pos |
28552875
fcp = MkCallAndBlanketPos(fc, pos) and
2856-
fc.resolveCallTargetBlanketLikeCandidate(impl, pos, blanketPath, blanketTypeParam)
2876+
fc.resolveCallTargetBlanketLikeCandidate(impl, pos, blanketPath, blanketTypeParam) and
2877+
// Only apply blanket implementations when no other implementations are possible;
2878+
// this is to account for codebases that use the (unstable) specialization feature
2879+
// (https://rust-lang.github.io/rfcs/1210-impl-specialization.html)
2880+
(fc.hasNoCompatibleNonBlanketTarget() or not impl.isBlanketImplementation())
28572881
)
28582882
}
28592883
}
@@ -2888,37 +2912,24 @@ private module NonMethodResolution {
28882912
private module ArgIsInstantiationOfBlanketParam =
28892913
ArgIsInstantiationOf<CallAndBlanketPos, ArgIsInstantiationOfBlanketParamInput>;
28902914

2891-
private module NonMethodArgsAreInstantiationsOfInput implements ArgsAreInstantiationsOfInputSig {
2915+
private module NonMethodArgsAreInstantiationsOfBlanketInput implements
2916+
ArgsAreInstantiationsOfInputSig
2917+
{
28922918
predicate toCheck(ImplOrTraitItemNode i, Function f, FunctionPosition pos, AssocFunctionType t) {
28932919
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-
)
2920+
exists(Type t0 | typeCanBeUsedForDisambiguation(t0) |
2921+
FunctionOverloading::functionResolutionDependsOnArgument(i, f, pos, _, t0)
29052922
or
2906-
// match against the trait function itself
2907-
exists(Trait trait |
2908-
FunctionOverloading::traitTypeParameterOccurrence(trait, f, _, pos, _,
2909-
TSelfTypeParameter(trait))
2910-
)
2923+
traitFunctionDependsOnPos(_, _, pos, t0, i, f)
29112924
)
29122925
}
29132926

2914-
class Call extends NonMethodCall {
2927+
final class Call extends NonMethodCall {
29152928
Type getArgType(FunctionPosition pos, TypePath path) {
29162929
result = inferType(this.getNodeAt(pos), path)
29172930
}
29182931

2919-
predicate hasTargetCand(ImplOrTraitItemNode i, Function f) {
2920-
f = this.resolveAssocCallTargetCand(i)
2921-
or
2932+
predicate hasTraitResolvedCand(ImplOrTraitItemNode i, Function f) {
29222933
exists(TraitItemNode trait, NonMethodFunction resolved, ImplItemNode i1, Function f1 |
29232934
this.hasTraitResolved(trait, resolved) and
29242935
traitFunctionDependsOnPos(trait, resolved, _, _, i1, f1)
@@ -2930,11 +2941,45 @@ private module NonMethodResolution {
29302941
i = trait
29312942
)
29322943
}
2944+
2945+
predicate hasTargetCand(ImplOrTraitItemNode i, Function f) {
2946+
f = this.resolveCallTargetBlanketCand(i)
2947+
or
2948+
this.hasTraitResolvedCand(i, f) and
2949+
BlanketImplementation::isBlanketLike(i, _, _)
2950+
}
2951+
}
2952+
}
2953+
2954+
private module NonMethodArgsAreInstantiationsOfBlanket =
2955+
ArgsAreInstantiationsOf<NonMethodArgsAreInstantiationsOfBlanketInput>;
2956+
2957+
private module NonMethodArgsAreInstantiationsOfNonBlanketInput implements
2958+
ArgsAreInstantiationsOfInputSig
2959+
{
2960+
predicate toCheck(ImplOrTraitItemNode i, Function f, FunctionPosition pos, AssocFunctionType t) {
2961+
NonMethodArgsAreInstantiationsOfBlanketInput::toCheck(i, f, pos, t)
2962+
or
2963+
// match against the trait function itself
2964+
t.appliesTo(f, i, pos) and
2965+
exists(Trait trait |
2966+
FunctionOverloading::traitTypeParameterOccurrence(trait, f, _, pos, _,
2967+
TSelfTypeParameter(trait))
2968+
)
2969+
}
2970+
2971+
class Call extends NonMethodArgsAreInstantiationsOfBlanketInput::Call {
2972+
predicate hasTargetCand(ImplOrTraitItemNode i, Function f) {
2973+
f = this.resolveCallTargetNonBlanketCand(i)
2974+
or
2975+
this.hasTraitResolvedCand(i, f) and
2976+
not BlanketImplementation::isBlanketLike(i, _, _)
2977+
}
29332978
}
29342979
}
29352980

2936-
private module NonMethodArgsAreInstantiationsOf =
2937-
ArgsAreInstantiationsOf<NonMethodArgsAreInstantiationsOfInput>;
2981+
private module NonMethodArgsAreInstantiationsOfNonBlanket =
2982+
ArgsAreInstantiationsOf<NonMethodArgsAreInstantiationsOfNonBlanketInput>;
29382983
}
29392984

29402985
abstract private class TupleLikeConstructor extends Addressable {

0 commit comments

Comments
 (0)