Skip to content

Commit 2c6e29a

Browse files
committed
Rust: Compute incompatible blanket implementations
1 parent 6e0092a commit 2c6e29a

File tree

5 files changed

+212
-44
lines changed

5 files changed

+212
-44
lines changed

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

Lines changed: 131 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -904,14 +904,14 @@ private predicate assocFunctionInfo(
904904

905905
/**
906906
* Holds if function `f` with the name `name` and the arity `arity` exists in
907-
* blanket implementation `impl` of `trait`, and the type at position
907+
* blanket (like) implementation `impl` of `trait`, and the type at position
908908
* `pos` is `t`.
909909
*
910910
* `blanketPath` points to the type `blanketTypeParam` inside `t`, which
911911
* is the type parameter used in the blanket implementation.
912912
*/
913913
pragma[nomagic]
914-
private predicate functionInfoBlanket(
914+
private predicate functionInfoBlanketLike(
915915
Function f, string name, int arity, ImplItemNode impl, Trait trait, FunctionTypePosition pos,
916916
AssocFunctionType t, TypePath blanketPath, TypeParam blanketTypeParam
917917
) {
@@ -1030,19 +1030,20 @@ private module MethodResolution {
10301030

10311031
/**
10321032
* Holds if method `m` with the name `name` and the arity `arity` exists in
1033-
* blanket implementation `impl` of `trait`, and the type of the `self`
1033+
* blanket (like) implementation `impl` of `trait`, and the type of the `self`
10341034
* parameter is `selfType`.
10351035
*
10361036
* `blanketPath` points to the type `blanketTypeParam` inside `selfType`, which
10371037
* is the type parameter used in the blanket implementation.
10381038
*/
10391039
pragma[nomagic]
1040-
private predicate methodInfoBlanket(
1040+
private predicate methodInfoBlanketLike(
10411041
Method m, string name, int arity, ImplItemNode impl, Trait trait, AssocFunctionType selfType,
10421042
TypePath blanketPath, TypeParam blanketTypeParam
10431043
) {
10441044
exists(FunctionTypePosition pos |
1045-
functionInfoBlanket(m, name, arity, impl, trait, pos, selfType, blanketPath, blanketTypeParam) and
1045+
functionInfoBlanketLike(m, name, arity, impl, trait, pos, selfType, blanketPath,
1046+
blanketTypeParam) and
10461047
pos.isSelf()
10471048
)
10481049
}
@@ -1116,8 +1117,8 @@ private module MethodResolution {
11161117
}
11171118

11181119
/**
1119-
* Holds if method call `mc` may target a method in blanket implementation `i`
1120-
* with `self` parameter having type `selfType`.
1120+
* Holds if method call `mc` may target a method in blanket (like) implementation
1121+
* `impl` with `self` parameter having type `selfType`.
11211122
*
11221123
* `blanketPath` points to the type `blanketTypeParam` inside `selfType`, which
11231124
* is the type parameter used in the blanket implementation.
@@ -1128,13 +1129,13 @@ private module MethodResolution {
11281129
*/
11291130
bindingset[mc]
11301131
pragma[inline_late]
1131-
private predicate methodCallBlanketCandidate(
1132+
private predicate methodCallBlanketLikeCandidate(
11321133
MethodCall mc, Method m, ImplItemNode impl, AssocFunctionType self, TypePath blanketPath,
11331134
TypeParam blanketTypeParam
11341135
) {
11351136
exists(string name, int arity |
11361137
mc.hasNameAndArity(name, arity) and
1137-
methodInfoBlanket(m, name, arity, impl, _, self, blanketPath, blanketTypeParam)
1138+
methodInfoBlanketLike(m, name, arity, impl, _, self, blanketPath, blanketTypeParam)
11381139
|
11391140
methodCallVisibleImplTraitCandidate(mc, impl)
11401141
or
@@ -1190,7 +1191,7 @@ private module MethodResolution {
11901191
or
11911192
this.supportsAutoDerefAndBorrow() and
11921193
exists(TypePath path0, Type t0, string derefChain0 |
1193-
this.hasNoCompatibleTarget(derefChain0, _) and
1194+
this.hasNoCompatibleTargetBorrow(derefChain0, _) and
11941195
t0 = this.getACandidateReceiverTypeAtNoBorrow(path0, derefChain0)
11951196
|
11961197
path0.isCons(TRefTypeParameter(), path) and
@@ -1219,6 +1220,21 @@ private module MethodResolution {
12191220
derefChainBorrow = ";"
12201221
}
12211222

1223+
/**
1224+
* Holds if the method inside blanket-like implementation `i` with matching name
1225+
* and arity can be ruled out as a target of this call, either because the candidate
1226+
* receiver type represented by `derefChainBorrow` is incompatible with the `self`
1227+
* parameter type, or because the blanket constraint is not satisfied.
1228+
*/
1229+
pragma[nomagic]
1230+
private predicate hasIncompatibleBlanketLikeTarget(ImplItemNode i, string derefChainBorrow) {
1231+
ReceiverIsNotInstantiationOfBlanketLikeSelfParam::argIsNotInstantiationOf(MkMethodCallCand(this,
1232+
derefChainBorrow), i, _)
1233+
or
1234+
ReceiverSatisfiesBlanketLikeConstraint::satisfiesNotBlanketConstraint(MkMethodCallCand(this,
1235+
derefChainBorrow), i)
1236+
}
1237+
12221238
/**
12231239
* Same as `getACandidateReceiverTypeAt`, but with traits substituted in for types
12241240
* with trait bounds.
@@ -1236,10 +1252,9 @@ private module MethodResolution {
12361252
}
12371253

12381254
bindingset[strippedTypePath, strippedType, derefChainBorrow]
1239-
private predicate hasNoCompatibleTargetCheck(
1255+
private predicate hasNoCompatibleNonBlanketLikeTargetCheck(
12401256
TypePath strippedTypePath, Type strippedType, string derefChainBorrow
12411257
) {
1242-
// todo: also check that all blanket implementation candidates are incompatible
12431258
forall(ImplOrTraitItemNode i |
12441259
methodCallNonBlanketCandidate(this, _, i, _, strippedTypePath, strippedType)
12451260
or
@@ -1249,6 +1264,28 @@ private module MethodResolution {
12491264
)
12501265
}
12511266

1267+
bindingset[strippedTypePath, strippedType, derefChainBorrow]
1268+
private predicate hasNoCompatibleTargetCheck(
1269+
TypePath strippedTypePath, Type strippedType, string derefChainBorrow
1270+
) {
1271+
this.hasNoCompatibleNonBlanketLikeTargetCheck(strippedTypePath, strippedType, derefChainBorrow) and
1272+
forall(ImplItemNode i | methodCallBlanketLikeCandidate(this, _, i, _, _, _) |
1273+
this.hasIncompatibleBlanketLikeTarget(i, derefChainBorrow)
1274+
)
1275+
}
1276+
1277+
bindingset[strippedTypePath, strippedType, derefChainBorrow]
1278+
private predicate hasNoCompatibleNonBlanketTargetCheck(
1279+
TypePath strippedTypePath, Type strippedType, string derefChainBorrow
1280+
) {
1281+
this.hasNoCompatibleNonBlanketLikeTargetCheck(strippedTypePath, strippedType, derefChainBorrow) and
1282+
forall(ImplItemNode i |
1283+
methodCallBlanketLikeCandidate(this, _, i, _, _, _) and not i.isBlanketImplementation()
1284+
|
1285+
this.hasIncompatibleBlanketLikeTarget(i, derefChainBorrow)
1286+
)
1287+
}
1288+
12521289
/**
12531290
* Holds if the candidate receiver type represented by
12541291
* `derefChainBorrow = derefChain;` does not have a matching method target.
@@ -1259,7 +1296,7 @@ private module MethodResolution {
12591296
this.supportsAutoDerefAndBorrow()
12601297
or
12611298
// needed for the `hasNoCompatibleTarget` check in
1262-
// `SatisfiesBlanketConstraintInput::hasBlanketCandidate`
1299+
// `ReceiverSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate`
12631300
derefChain = ""
12641301
) and
12651302
exists(TypePath strippedTypePath, Type strippedType |
@@ -1270,13 +1307,35 @@ private module MethodResolution {
12701307
)
12711308
}
12721309

1310+
/**
1311+
* Holds if the candidate receiver type represented by
1312+
* `derefChainBorrow = derefChain;` does not have a matching non-blanket
1313+
* method target.
1314+
*/
1315+
pragma[nomagic]
1316+
predicate hasNoCompatibleNonBlanketTargetNoBorrow(string derefChain, string derefChainBorrow) {
1317+
(
1318+
this.supportsAutoDerefAndBorrow()
1319+
or
1320+
// needed for the `hasNoCompatibleTarget` check in
1321+
// `ReceiverSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate`
1322+
derefChain = ""
1323+
) and
1324+
exists(TypePath strippedTypePath, Type strippedType |
1325+
derefChainBorrow = derefChain + ";" and
1326+
not derefChain.matches("%.ref") and // no need to try a borrow if the last thing we did was a deref
1327+
strippedType = this.getComplexStrippedType(strippedTypePath, derefChainBorrow) and
1328+
this.hasNoCompatibleNonBlanketTargetCheck(strippedTypePath, strippedType, derefChainBorrow)
1329+
)
1330+
}
1331+
12731332
/**
12741333
* Holds if the candidate receiver type represented by
12751334
* `derefChainBorrow = derefChain;borrow` does not have a matching method
12761335
* target.
12771336
*/
12781337
pragma[nomagic]
1279-
predicate hasNoCompatibleTarget(string derefChain, string derefChainBorrow) {
1338+
predicate hasNoCompatibleTargetBorrow(string derefChain, string derefChainBorrow) {
12801339
exists(TypePath strippedTypePath, Type strippedType |
12811340
derefChainBorrow = derefChain + ";borrow" and
12821341
this.hasNoCompatibleTargetNoBorrow(derefChain, _) and
@@ -1285,6 +1344,21 @@ private module MethodResolution {
12851344
)
12861345
}
12871346

1347+
/**
1348+
* Holds if the candidate receiver type represented by
1349+
* `derefChainBorrow = derefChain;borrow` does not have a matching non-blanket
1350+
* method target.
1351+
*/
1352+
pragma[nomagic]
1353+
predicate hasNoCompatibleNonBlanketTargetBorrow(string derefChain, string derefChainBorrow) {
1354+
exists(TypePath strippedTypePath, Type strippedType |
1355+
derefChainBorrow = derefChain + ";borrow" and
1356+
this.hasNoCompatibleTargetNoBorrow(derefChain, _) and
1357+
strippedType = this.getComplexStrippedType(strippedTypePath, derefChainBorrow) and
1358+
this.hasNoCompatibleNonBlanketTargetCheck(strippedTypePath, strippedType, derefChainBorrow)
1359+
)
1360+
}
1361+
12881362
/**
12891363
* Gets a [candidate receiver type][1] of this method call at `path`.
12901364
*
@@ -1477,10 +1551,10 @@ private module MethodResolution {
14771551
}
14781552

14791553
pragma[nomagic]
1480-
predicate hasNoCompatibleTarget() {
1481-
mc_.hasNoCompatibleTarget(_, derefChainBorrow)
1554+
predicate hasNoCompatibleNonBlanketTarget() {
1555+
mc_.hasNoCompatibleNonBlanketTargetBorrow(_, derefChainBorrow)
14821556
or
1483-
mc_.hasNoCompatibleTargetNoBorrow(_, derefChainBorrow)
1557+
mc_.hasNoCompatibleNonBlanketTargetNoBorrow(_, derefChainBorrow)
14841558
}
14851559

14861560
pragma[nomagic]
@@ -1563,20 +1637,20 @@ private module MethodResolution {
15631637
Location getLocation() { result = mc_.getLocation() }
15641638
}
15651639

1566-
private module ReceiverSatisfiesBlanketConstraintInput implements
1640+
private module ReceiverSatisfiesBlanketLikeConstraintInput implements
15671641
BlanketImplementation::SatisfiesBlanketConstraintInputSig<MethodCallCand>
15681642
{
15691643
pragma[nomagic]
15701644
predicate hasBlanketCandidate(
15711645
MethodCallCand mcc, ImplItemNode impl, TypePath blanketPath, TypeParam blanketTypeParam
15721646
) {
1573-
exists(MethodCall mc, string name, int arity |
1574-
mcc.hasSignature(mc, _, _, name, arity) and
1575-
methodCallBlanketCandidate(mc, _, impl, _, blanketPath, blanketTypeParam) and
1647+
exists(MethodCall mc |
1648+
mc = mcc.getMethodCall() and
1649+
methodCallBlanketLikeCandidate(mc, _, impl, _, blanketPath, blanketTypeParam) and
15761650
// Only apply blanket implementations when no other implementations are possible;
15771651
// this is to account for codebases that use the (unstable) specialization feature
15781652
// (https://rust-lang.github.io/rfcs/1210-impl-specialization.html)
1579-
mcc.hasNoCompatibleTarget()
1653+
(mcc.hasNoCompatibleNonBlanketTarget() or not impl.isBlanketImplementation())
15801654
|
15811655
mcc.hasNoBorrow()
15821656
or
@@ -1585,9 +1659,9 @@ private module MethodResolution {
15851659
}
15861660
}
15871661

1588-
private module ReceiverSatisfiesBlanketConstraint =
1662+
private module ReceiverSatisfiesBlanketLikeConstraint =
15891663
BlanketImplementation::SatisfiesBlanketConstraint<MethodCallCand,
1590-
ReceiverSatisfiesBlanketConstraintInput>;
1664+
ReceiverSatisfiesBlanketLikeConstraintInput>;
15911665

15921666
/**
15931667
* A configuration for matching the type of a receiver against the type of
@@ -1608,8 +1682,8 @@ private module MethodResolution {
16081682
|
16091683
methodCallNonBlanketCandidate(mc, m, i, selfType, strippedTypePath, strippedType)
16101684
or
1611-
methodCallBlanketCandidate(mc, m, i, selfType, _, _) and
1612-
ReceiverSatisfiesBlanketConstraint::satisfiesBlanketConstraint(mcc, i)
1685+
methodCallBlanketLikeCandidate(mc, m, i, selfType, _, _) and
1686+
ReceiverSatisfiesBlanketLikeConstraint::satisfiesBlanketConstraint(mcc, i)
16131687
)
16141688
}
16151689

@@ -1634,6 +1708,30 @@ private module MethodResolution {
16341708
private module ReceiverIsInstantiationOfSelfParam =
16351709
ArgIsInstantiationOf<MethodCallCand, ReceiverIsInstantiationOfSelfParamInput>;
16361710

1711+
/**
1712+
* A configuration for anti-matching the type of a receiver against the type of
1713+
* a `self` parameter belonging to a blanket (like) implementation.
1714+
*/
1715+
private module ReceiverIsNotInstantiationOfBlanketLikeSelfParamInput implements
1716+
IsInstantiationOfInputSig<MethodCallCand, AssocFunctionType>
1717+
{
1718+
pragma[nomagic]
1719+
predicate potentialInstantiationOf(
1720+
MethodCallCand mcc, TypeAbstraction abs, AssocFunctionType constraint
1721+
) {
1722+
methodCallBlanketLikeCandidate(mcc.getMethodCall(), _, abs, constraint, _, _) and
1723+
if abs.(Impl).hasTrait()
1724+
then
1725+
// inherent methods take precedence over trait methods, so only allow
1726+
// trait methods when there are no matching inherent methods
1727+
mcc.hasNoInherentTarget()
1728+
else any()
1729+
}
1730+
}
1731+
1732+
private module ReceiverIsNotInstantiationOfBlanketLikeSelfParam =
1733+
ArgIsInstantiationOf<MethodCallCand, ReceiverIsNotInstantiationOfBlanketLikeSelfParamInput>;
1734+
16371735
/**
16381736
* A configuration for matching the type qualifier of a method call
16391737
* against the type being implemented in an `impl` block. For example,
@@ -1687,10 +1785,6 @@ private module MethodResolution {
16871785
ReceiverIsInstantiationOfSelfParamInput::potentialInstantiationOf0(mcc, abs, constraint) and
16881786
abs = any(Impl i | not i.hasTrait())
16891787
}
1690-
1691-
predicate relevantConstraint(AssocFunctionType constraint) {
1692-
methodInfo(_, _, _, _, constraint, _, _)
1693-
}
16941788
}
16951789

16961790
private module ReceiverIsNotInstantiationOfInherentSelfParam =
@@ -1946,18 +2040,18 @@ private module NonMethodResolution {
19462040
}
19472041

19482042
pragma[nomagic]
1949-
private predicate functionInfoBlanketRelevantPos(
2043+
private predicate functionInfoBlanketLikeRelevantPos(
19502044
NonMethodFunction f, string name, int arity, ImplItemNode impl, Trait trait,
19512045
FunctionTypePosition pos, AssocFunctionType t, TypePath blanketPath, TypeParam blanketTypeParam
19522046
) {
1953-
functionInfoBlanket(f, name, arity, impl, trait, pos, t, blanketPath, blanketTypeParam) and
2047+
functionInfoBlanketLike(f, name, arity, impl, trait, pos, t, blanketPath, blanketTypeParam) and
19542048
(
19552049
not pos.isReturn()
19562050
or
19572051
// We only check that the context of the call provides relevant type information
19582052
// when no argument can
19592053
not exists(FunctionTypePosition pos0 |
1960-
functionInfoBlanket(f, name, arity, impl, trait, pos0, _, _, _) and
2054+
functionInfoBlanketLike(f, name, arity, impl, trait, pos0, _, _, _) and
19612055
not pos0.isReturn()
19622056
)
19632057
)
@@ -1967,7 +2061,7 @@ private module NonMethodResolution {
19672061
private predicate blanketCallTraitCandidate(Element fc, Trait trait) {
19682062
exists(string name, int arity |
19692063
fc.(NonMethodCall).hasNameAndArity(name, arity) and
1970-
functionInfoBlanketRelevantPos(_, name, arity, _, trait, _, _, _, _)
2064+
functionInfoBlanketLikeRelevantPos(_, name, arity, _, trait, _, _, _, _)
19712065
|
19722066
not fc.(Call).hasTrait()
19732067
or
@@ -2040,7 +2134,7 @@ private module NonMethodResolution {
20402134
exists(string name, int arity, Trait trait, AssocFunctionType t |
20412135
this.hasNameAndArity(name, arity) and
20422136
exists(this.getTypeAt(pos, blanketPath)) and
2043-
functionInfoBlanketRelevantPos(_, name, arity, impl, trait, pos, t, blanketPath,
2137+
functionInfoBlanketLikeRelevantPos(_, name, arity, impl, trait, pos, t, blanketPath,
20442138
blanketTypeParam) and
20452139
BlanketTraitIsVisible::traitIsVisible(this, trait)
20462140
)
@@ -2126,12 +2220,12 @@ private module NonMethodResolution {
21262220
exists(FunctionTypePosition pos |
21272221
ArgSatisfiesBlanketConstraint::satisfiesBlanketConstraint(fcp, abs) and
21282222
fcp = MkCallAndBlanketPos(_, pos) and
2129-
functionInfoBlanketRelevantPos(_, _, _, abs, _, pos, constraint, _, _)
2223+
functionInfoBlanketLikeRelevantPos(_, _, _, abs, _, pos, constraint, _, _)
21302224
)
21312225
}
21322226

21332227
predicate relevantConstraint(AssocFunctionType constraint) {
2134-
functionInfoBlanketRelevantPos(_, _, _, _, _, _, constraint, _, _)
2228+
functionInfoBlanketLikeRelevantPos(_, _, _, _, _, _, constraint, _, _)
21352229
}
21362230
}
21372231

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,17 @@ module SatisfiesBlanketConstraint<
132132
SatisfiesBlanketConstraint::satisfiesConstraintType(ato, TTrait(traitBound), _, _)
133133
)
134134
}
135+
136+
/**
137+
* Holds if the argument type `at` does _not_ satisfy the first non-trivial blanket
138+
* constraint of `impl`.
139+
*/
140+
pragma[nomagic]
141+
predicate satisfiesNotBlanketConstraint(ArgumentType at, ImplItemNode impl) {
142+
exists(ArgumentTypeAndBlanketOffset ato, Trait traitBound |
143+
ato = MkArgumentTypeAndBlanketOffset(at, _) and
144+
SatisfiesBlanketConstraintInput::relevantConstraint(ato, impl, traitBound) and
145+
SatisfiesBlanketConstraint::satisfiesNotConstraint(ato, TTrait(traitBound))
146+
)
147+
}
135148
}

rust/ql/test/library-tests/type-inference/CONSISTENCY/PathResolutionConsistency.expected

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
multipleCallTargets
2-
| blanket_impl.rs:257:18:257:27 | ... .m2() |
3-
| blanket_impl.rs:259:18:259:24 | S1.m4() |
42
| dereference.rs:69:15:69:24 | e1.deref() |
53
| dereference.rs:182:17:182:26 | ... .foo() |
64
| dereference.rs:183:17:183:23 | S.foo() |

0 commit comments

Comments
 (0)