Skip to content

Commit 326464f

Browse files
committed
Rust: Remove restriction that blanket(-like) impls must have a constraint
1 parent 06b99b2 commit 326464f

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
@@ -58,12 +58,26 @@ private predicate implSiblings(TraitItemNode trait, Impl impl1, Impl impl2) {
5858
)
5959
}
6060

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

6882
/**
6983
* 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
@@ -356,7 +356,7 @@ module ArgsAreInstantiationsOf<ArgsAreInstantiationsOfInputSig Input> {
356356
string toString() { result = call.toString() + " [arg " + pos + "]" }
357357
}
358358

359-
private module ArgIsInstantiationOfInput implements
359+
private module ArgIsInstantiationOfToIndexInput implements
360360
IsInstantiationOfInputSig<CallAndPos, AssocFunctionType>
361361
{
362362
pragma[nomagic]
@@ -389,7 +389,7 @@ module ArgsAreInstantiationsOf<ArgsAreInstantiationsOfInputSig Input> {
389389
}
390390

391391
private module ArgIsInstantiationOfToIndex =
392-
ArgIsInstantiationOf<CallAndPos, ArgIsInstantiationOfInput>;
392+
ArgIsInstantiationOf<CallAndPos, ArgIsInstantiationOfToIndexInput>;
393393

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

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

Lines changed: 79 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,6 +1289,13 @@ private class BorrowKind extends TBorrowKind {
12891289
}
12901290
}
12911291

1292+
// for now, we do not handle ambiguous targets when one of the types it iself
1293+
// a constrained type parameter; we should be checking the constraints in this case
1294+
private predicate typeCanBeUsedForDisambiguation(Type t) {
1295+
not t instanceof TypeParameter or
1296+
t.(TypeParamTypeParameter).getTypeParam() = any(TypeParam tp | not exists(tp.getATypeBound()))
1297+
}
1298+
12921299
/**
12931300
* Provides logic for resolving calls to methods.
12941301
*
@@ -2384,10 +2391,7 @@ private module MethodResolution {
23842391
exists(TypePath path, Type t0 |
23852392
FunctionOverloading::functionResolutionDependsOnArgument(i, f, pos, path, t0) and
23862393
t.appliesTo(f, i, pos) and
2387-
// for now, we do not handle ambiguous targets when one of the types it iself
2388-
// a type parameter; we should be checking the constraints on that type parameter
2389-
// in this case
2390-
not t0 instanceof TypeParameter
2394+
typeCanBeUsedForDisambiguation(t0)
23912395
)
23922396
}
23932397

@@ -2746,7 +2750,7 @@ private module NonMethodResolution {
27462750
* Gets the blanket function that this call may resolve to, if any.
27472751
*/
27482752
pragma[nomagic]
2749-
private NonMethodFunction resolveCallTargetBlanketCand(ImplItemNode impl) {
2753+
NonMethodFunction resolveCallTargetBlanketCand(ImplItemNode impl) {
27502754
exists(string name |
27512755
this.hasNameAndArity(pragma[only_bind_into](name), _) and
27522756
ArgIsInstantiationOfBlanketParam::argIsInstantiationOf(MkCallAndBlanketPos(this, _), impl, _) and
@@ -2761,12 +2765,11 @@ private module NonMethodResolution {
27612765
predicate hasTrait() { exists(this.getTrait()) }
27622766

27632767
pragma[nomagic]
2764-
NonMethodFunction resolveAssocCallTargetCand(ImplItemNode i) {
2768+
NonMethodFunction resolveCallTargetNonBlanketCand(ImplItemNode i) {
27652769
not this.hasTrait() and
27662770
result = this.getPathResolutionResolved() and
2767-
result = i.getASuccessor(_)
2768-
or
2769-
result = this.resolveCallTargetBlanketCand(i)
2771+
result = i.getASuccessor(_) and
2772+
FunctionOverloading::functionResolutionDependsOnArgument(_, result, _, _, _)
27702773
}
27712774

27722775
AstNode getNodeAt(FunctionPosition pos) {
@@ -2798,6 +2801,21 @@ private module NonMethodResolution {
27982801
trait = this.getTrait()
27992802
}
28002803

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

28222842
pragma[nomagic]
@@ -2855,7 +2875,11 @@ private module NonMethodResolution {
28552875
) {
28562876
exists(NonMethodCall fc, FunctionPosition pos |
28572877
fcp = MkCallAndBlanketPos(fc, pos) and
2858-
fc.resolveCallTargetBlanketLikeCandidate(impl, pos, blanketPath, blanketTypeParam)
2878+
fc.resolveCallTargetBlanketLikeCandidate(impl, pos, blanketPath, blanketTypeParam) and
2879+
// Only apply blanket implementations when no other implementations are possible;
2880+
// this is to account for codebases that use the (unstable) specialization feature
2881+
// (https://rust-lang.github.io/rfcs/1210-impl-specialization.html)
2882+
(fc.hasNoCompatibleNonBlanketTarget() or not impl.isBlanketImplementation())
28592883
)
28602884
}
28612885
}
@@ -2890,37 +2914,24 @@ private module NonMethodResolution {
28902914
private module ArgIsInstantiationOfBlanketParam =
28912915
ArgIsInstantiationOf<CallAndBlanketPos, ArgIsInstantiationOfBlanketParamInput>;
28922916

2893-
private module NonMethodArgsAreInstantiationsOfInput implements ArgsAreInstantiationsOfInputSig {
2917+
private module NonMethodArgsAreInstantiationsOfBlanketInput implements
2918+
ArgsAreInstantiationsOfInputSig
2919+
{
28942920
predicate toCheck(ImplOrTraitItemNode i, Function f, FunctionPosition pos, AssocFunctionType t) {
28952921
t.appliesTo(f, i, pos) and
2896-
(
2897-
exists(Type t0 |
2898-
// for now, we do not handle ambiguous targets when one of the types it iself
2899-
// a type parameter; we should be checking the constraints on that type parameter
2900-
// in this case
2901-
not t0 instanceof TypeParameter
2902-
|
2903-
FunctionOverloading::functionResolutionDependsOnArgument(i, f, pos, _, t0)
2904-
or
2905-
traitFunctionDependsOnPos(_, _, pos, t0, i, f)
2906-
)
2922+
exists(Type t0 | typeCanBeUsedForDisambiguation(t0) |
2923+
FunctionOverloading::functionResolutionDependsOnArgument(i, f, pos, _, t0)
29072924
or
2908-
// match against the trait function itself
2909-
exists(Trait trait |
2910-
FunctionOverloading::traitTypeParameterOccurrence(trait, f, _, pos, _,
2911-
TSelfTypeParameter(trait))
2912-
)
2925+
traitFunctionDependsOnPos(_, _, pos, t0, i, f)
29132926
)
29142927
}
29152928

2916-
class Call extends NonMethodCall {
2929+
final class Call extends NonMethodCall {
29172930
Type getArgType(FunctionPosition pos, TypePath path) {
29182931
result = inferType(this.getNodeAt(pos), path)
29192932
}
29202933

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

2938-
private module NonMethodArgsAreInstantiationsOf =
2939-
ArgsAreInstantiationsOf<NonMethodArgsAreInstantiationsOfInput>;
2983+
private module NonMethodArgsAreInstantiationsOfNonBlanket =
2984+
ArgsAreInstantiationsOf<NonMethodArgsAreInstantiationsOfNonBlanketInput>;
29402985
}
29412986

29422987
abstract private class TupleLikeConstructor extends Addressable {

0 commit comments

Comments
 (0)