Skip to content

Commit 9e0f977

Browse files
committed
Rust: Compute incompatible blanket implementations
1 parent 0c1d201 commit 9e0f977

File tree

5 files changed

+218
-49
lines changed

5 files changed

+218
-49
lines changed

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

Lines changed: 137 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -901,14 +901,14 @@ private predicate assocFunctionInfo(
901901

902902
/**
903903
* Holds if function `f` with the name `name` and the arity `arity` exists in
904-
* blanket implementation `impl` of `trait`, and the type at position
904+
* blanket (like) implementation `impl` of `trait`, and the type at position
905905
* `pos` is `t`.
906906
*
907907
* `blanketPath` points to the type `blanketTypeParam` inside `t`, which
908908
* is the type parameter used in the blanket implementation.
909909
*/
910910
pragma[nomagic]
911-
private predicate functionInfoBlanket(
911+
private predicate functionInfoBlanketLike(
912912
Function f, string name, int arity, ImplItemNode impl, Trait trait, FunctionPosition pos,
913913
AssocFunctionType t, TypePath blanketPath, TypeParam blanketTypeParam
914914
) {
@@ -1027,19 +1027,20 @@ private module MethodResolution {
10271027

10281028
/**
10291029
* Holds if method `m` with the name `name` and the arity `arity` exists in
1030-
* blanket implementation `impl` of `trait`, and the type of the `self`
1030+
* blanket (like) implementation `impl` of `trait`, and the type of the `self`
10311031
* parameter is `selfType`.
10321032
*
10331033
* `blanketPath` points to the type `blanketTypeParam` inside `selfType`, which
10341034
* is the type parameter used in the blanket implementation.
10351035
*/
10361036
pragma[nomagic]
1037-
private predicate methodInfoBlanket(
1037+
private predicate methodInfoBlanketLike(
10381038
Method m, string name, int arity, ImplItemNode impl, Trait trait, AssocFunctionType selfType,
10391039
TypePath blanketPath, TypeParam blanketTypeParam
10401040
) {
10411041
exists(FunctionPosition pos |
1042-
functionInfoBlanket(m, name, arity, impl, trait, pos, selfType, blanketPath, blanketTypeParam) and
1042+
functionInfoBlanketLike(m, name, arity, impl, trait, pos, selfType, blanketPath,
1043+
blanketTypeParam) and
10431044
pos.isSelf()
10441045
)
10451046
}
@@ -1113,8 +1114,8 @@ private module MethodResolution {
11131114
}
11141115

11151116
/**
1116-
* Holds if method call `mc` may target a method in blanket implementation `i`
1117-
* with `self` parameter having type `selfType`.
1117+
* Holds if method call `mc` may target a method in blanket (like) implementation
1118+
* `impl` with `self` parameter having type `selfType`.
11181119
*
11191120
* `blanketPath` points to the type `blanketTypeParam` inside `selfType`, which
11201121
* is the type parameter used in the blanket implementation.
@@ -1125,13 +1126,13 @@ private module MethodResolution {
11251126
*/
11261127
bindingset[mc]
11271128
pragma[inline_late]
1128-
private predicate methodCallBlanketCandidate(
1129+
private predicate methodCallBlanketLikeCandidate(
11291130
MethodCall mc, Method m, ImplItemNode impl, AssocFunctionType self, TypePath blanketPath,
11301131
TypeParam blanketTypeParam
11311132
) {
11321133
exists(string name, int arity |
11331134
mc.hasNameAndArity(name, arity) and
1134-
methodInfoBlanket(m, name, arity, impl, _, self, blanketPath, blanketTypeParam)
1135+
methodInfoBlanketLike(m, name, arity, impl, _, self, blanketPath, blanketTypeParam)
11351136
|
11361137
methodCallVisibleImplTraitCandidate(mc, impl)
11371138
or
@@ -1216,6 +1217,23 @@ private module MethodResolution {
12161217
borrow), i, _)
12171218
}
12181219

1220+
/**
1221+
* Holds if the method inside blanket-like implementation `impl` with matching name
1222+
* and arity can be ruled out as a target of this call, either because the candidate
1223+
* receiver type represented by `derefChain` and `borrow` is incompatible with the `self`
1224+
* parameter type, or because the blanket constraint is not satisfied.
1225+
*/
1226+
pragma[nomagic]
1227+
private predicate hasIncompatibleBlanketLikeTarget(
1228+
ImplItemNode impl, string derefChain, boolean borrow
1229+
) {
1230+
ReceiverIsNotInstantiationOfBlanketLikeSelfParam::argIsNotInstantiationOf(MkMethodCallCand(this,
1231+
derefChain, borrow), impl, _)
1232+
or
1233+
ReceiverSatisfiesBlanketLikeConstraint::satisfiesNotBlanketConstraint(MkMethodCallCand(this,
1234+
derefChain, borrow), impl)
1235+
}
1236+
12191237
/**
12201238
* Same as `getACandidateReceiverTypeAt`, but with traits substituted in for types
12211239
* with trait bounds.
@@ -1234,18 +1252,41 @@ private module MethodResolution {
12341252
isComplexRootStripped(strippedTypePath, result)
12351253
}
12361254

1237-
bindingset[strippedTypePath, strippedType, derefChain, borrow]
1238-
private predicate hasNoCompatibleTargetCheck(
1255+
bindingset[derefChain, borrow, strippedTypePath, strippedType]
1256+
private predicate hasNoCompatibleNonBlanketLikeTargetCheck(
12391257
string derefChain, boolean borrow, TypePath strippedTypePath, Type strippedType
12401258
) {
1241-
// todo: also check that all blanket implementation candidates are incompatible
12421259
forall(ImplOrTraitItemNode i |
12431260
methodCallNonBlanketCandidate(this, _, i, _, strippedTypePath, strippedType)
12441261
|
12451262
this.hasIncompatibleTarget(i, derefChain, borrow)
12461263
)
12471264
}
12481265

1266+
bindingset[derefChain, borrow, strippedTypePath, strippedType]
1267+
private predicate hasNoCompatibleTargetCheck(
1268+
string derefChain, boolean borrow, TypePath strippedTypePath, Type strippedType
1269+
) {
1270+
this.hasNoCompatibleNonBlanketLikeTargetCheck(derefChain, borrow, strippedTypePath,
1271+
strippedType) and
1272+
forall(ImplItemNode i | methodCallBlanketLikeCandidate(this, _, i, _, _, _) |
1273+
this.hasIncompatibleBlanketLikeTarget(i, derefChain, borrow)
1274+
)
1275+
}
1276+
1277+
bindingset[derefChain, borrow, strippedTypePath, strippedType]
1278+
private predicate hasNoCompatibleNonBlanketTargetCheck(
1279+
string derefChain, boolean borrow, TypePath strippedTypePath, Type strippedType
1280+
) {
1281+
this.hasNoCompatibleNonBlanketLikeTargetCheck(derefChain, borrow, strippedTypePath,
1282+
strippedType) and
1283+
forall(ImplItemNode i |
1284+
methodCallBlanketLikeCandidate(this, _, i, _, _, _) and not i.isBlanketImplementation()
1285+
|
1286+
this.hasIncompatibleBlanketLikeTarget(i, derefChain, borrow)
1287+
)
1288+
}
1289+
12491290
/**
12501291
* Holds if the candidate receiver type represented by `derefChain` does not
12511292
* have a matching method target.
@@ -1256,7 +1297,7 @@ private module MethodResolution {
12561297
this.supportsAutoDerefAndBorrow()
12571298
or
12581299
// needed for the `hasNoCompatibleTarget` check in
1259-
// `SatisfiesBlanketConstraintInput::hasBlanketCandidate`
1300+
// `ReceiverSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate`
12601301
derefChain = ""
12611302
) and
12621303
exists(TypePath strippedTypePath, Type strippedType |
@@ -1266,6 +1307,26 @@ private module MethodResolution {
12661307
)
12671308
}
12681309

1310+
/**
1311+
* Holds if the candidate receiver type represented by `derefChain` does not have
1312+
* a matching non-blanket method target.
1313+
*/
1314+
pragma[nomagic]
1315+
predicate hasNoCompatibleNonBlanketTargetNoBorrow(string derefChain) {
1316+
(
1317+
this.supportsAutoDerefAndBorrow()
1318+
or
1319+
// needed for the `hasNoCompatibleTarget` check in
1320+
// `ReceiverSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate`
1321+
derefChain = ""
1322+
) and
1323+
exists(TypePath strippedTypePath, Type strippedType |
1324+
not derefChain.matches("%.ref") and // no need to try a borrow if the last thing we did was a deref
1325+
strippedType = this.getComplexStrippedType(derefChain, false, strippedTypePath) and
1326+
this.hasNoCompatibleNonBlanketTargetCheck(derefChain, false, strippedTypePath, strippedType)
1327+
)
1328+
}
1329+
12691330
/**
12701331
* Holds if the candidate receiver type represented by `derefChain`, followed
12711332
* by a borrow, does not have a matching method target.
@@ -1275,7 +1336,21 @@ private module MethodResolution {
12751336
exists(TypePath strippedTypePath, Type strippedType |
12761337
this.hasNoCompatibleTargetNoBorrow(derefChain) and
12771338
strippedType = this.getComplexStrippedType(derefChain, true, strippedTypePath) and
1278-
this.hasNoCompatibleTargetCheck(derefChain, true, strippedTypePath, strippedType)
1339+
this.hasNoCompatibleNonBlanketLikeTargetCheck(derefChain, true, strippedTypePath,
1340+
strippedType)
1341+
)
1342+
}
1343+
1344+
/**
1345+
* Holds if the candidate receiver type represented by `derefChain`, followed
1346+
* by a borrow, does not have a matching non-blanket method target.
1347+
*/
1348+
pragma[nomagic]
1349+
predicate hasNoCompatibleNonBlanketTargetBorrow(string derefChain) {
1350+
exists(TypePath strippedTypePath, Type strippedType |
1351+
this.hasNoCompatibleTargetNoBorrow(derefChain) and
1352+
strippedType = this.getComplexStrippedType(derefChain, true, strippedTypePath) and
1353+
this.hasNoCompatibleNonBlanketTargetCheck(derefChain, true, strippedTypePath, strippedType)
12791354
)
12801355
}
12811356

@@ -1470,11 +1545,11 @@ private module MethodResolution {
14701545
}
14711546

14721547
pragma[nomagic]
1473-
predicate hasNoCompatibleTarget() {
1474-
mc_.hasNoCompatibleTargetBorrow(derefChain) and
1548+
predicate hasNoCompatibleNonBlanketTarget() {
1549+
mc_.hasNoCompatibleNonBlanketTargetBorrow(derefChain) and
14751550
borrow = true
14761551
or
1477-
mc_.hasNoCompatibleTargetNoBorrow(derefChain) and
1552+
mc_.hasNoCompatibleNonBlanketTargetNoBorrow(derefChain) and
14781553
borrow = false
14791554
}
14801555

@@ -1555,20 +1630,20 @@ private module MethodResolution {
15551630
Location getLocation() { result = mc_.getLocation() }
15561631
}
15571632

1558-
private module ReceiverSatisfiesBlanketConstraintInput implements
1633+
private module ReceiverSatisfiesBlanketLikeConstraintInput implements
15591634
BlanketImplementation::SatisfiesBlanketConstraintInputSig<MethodCallCand>
15601635
{
15611636
pragma[nomagic]
15621637
predicate hasBlanketCandidate(
15631638
MethodCallCand mcc, ImplItemNode impl, TypePath blanketPath, TypeParam blanketTypeParam
15641639
) {
1565-
exists(MethodCall mc, string name, int arity |
1566-
mcc.hasSignature(mc, _, _, name, arity) and
1567-
methodCallBlanketCandidate(mc, _, impl, _, blanketPath, blanketTypeParam) and
1640+
exists(MethodCall mc |
1641+
mc = mcc.getMethodCall() and
1642+
methodCallBlanketLikeCandidate(mc, _, impl, _, blanketPath, blanketTypeParam) and
15681643
// Only apply blanket implementations when no other implementations are possible;
15691644
// this is to account for codebases that use the (unstable) specialization feature
15701645
// (https://rust-lang.github.io/rfcs/1210-impl-specialization.html)
1571-
mcc.hasNoCompatibleTarget()
1646+
(mcc.hasNoCompatibleNonBlanketTarget() or not impl.isBlanketImplementation())
15721647
|
15731648
mcc.hasNoBorrow()
15741649
or
@@ -1577,9 +1652,9 @@ private module MethodResolution {
15771652
}
15781653
}
15791654

1580-
private module ReceiverSatisfiesBlanketConstraint =
1655+
private module ReceiverSatisfiesBlanketLikeConstraint =
15811656
BlanketImplementation::SatisfiesBlanketConstraint<MethodCallCand,
1582-
ReceiverSatisfiesBlanketConstraintInput>;
1657+
ReceiverSatisfiesBlanketLikeConstraintInput>;
15831658

15841659
/**
15851660
* A configuration for matching the type of a receiver against the type of
@@ -1600,8 +1675,8 @@ private module MethodResolution {
16001675
|
16011676
methodCallNonBlanketCandidate(mc, m, i, selfType, strippedTypePath, strippedType)
16021677
or
1603-
methodCallBlanketCandidate(mc, m, i, selfType, _, _) and
1604-
ReceiverSatisfiesBlanketConstraint::satisfiesBlanketConstraint(mcc, i)
1678+
methodCallBlanketLikeCandidate(mc, m, i, selfType, _, _) and
1679+
ReceiverSatisfiesBlanketLikeConstraint::satisfiesBlanketConstraint(mcc, i)
16051680
)
16061681
}
16071682

@@ -1626,6 +1701,30 @@ private module MethodResolution {
16261701
private module ReceiverIsInstantiationOfSelfParam =
16271702
ArgIsInstantiationOf<MethodCallCand, ReceiverIsInstantiationOfSelfParamInput>;
16281703

1704+
/**
1705+
* A configuration for anti-matching the type of a receiver against the type of
1706+
* a `self` parameter belonging to a blanket (like) implementation.
1707+
*/
1708+
private module ReceiverIsNotInstantiationOfBlanketLikeSelfParamInput implements
1709+
IsInstantiationOfInputSig<MethodCallCand, AssocFunctionType>
1710+
{
1711+
pragma[nomagic]
1712+
predicate potentialInstantiationOf(
1713+
MethodCallCand mcc, TypeAbstraction abs, AssocFunctionType constraint
1714+
) {
1715+
methodCallBlanketLikeCandidate(mcc.getMethodCall(), _, abs, constraint, _, _) and
1716+
if abs.(Impl).hasTrait()
1717+
then
1718+
// inherent methods take precedence over trait methods, so only allow
1719+
// trait methods when there are no matching inherent methods
1720+
mcc.hasNoInherentTarget()
1721+
else any()
1722+
}
1723+
}
1724+
1725+
private module ReceiverIsNotInstantiationOfBlanketLikeSelfParam =
1726+
ArgIsInstantiationOf<MethodCallCand, ReceiverIsNotInstantiationOfBlanketLikeSelfParamInput>;
1727+
16291728
/**
16301729
* A configuration for matching the type qualifier of a method call
16311730
* against the type being implemented in an `impl` block. For example,
@@ -1679,10 +1778,6 @@ private module MethodResolution {
16791778
ReceiverIsInstantiationOfSelfParamInput::potentialInstantiationOf0(mcc, abs, constraint) and
16801779
abs = any(Impl i | not i.hasTrait())
16811780
}
1682-
1683-
predicate relevantConstraint(AssocFunctionType constraint) {
1684-
methodInfo(_, _, _, _, constraint, _, _)
1685-
}
16861781
}
16871782

16881783
private module ReceiverIsNotInstantiationOfInherentSelfParam =
@@ -1948,37 +2043,37 @@ private module NonMethodResolution {
19482043
}
19492044

19502045
pragma[nomagic]
1951-
private predicate functionInfoBlanketRelevantPos(
2046+
private predicate functionInfoBlanketLikeRelevantPos(
19522047
NonMethodFunction f, string name, int arity, ImplItemNode impl, Trait trait,
19532048
FunctionPosition pos, AssocFunctionType t, TypePath blanketPath, TypeParam blanketTypeParam
19542049
) {
1955-
functionInfoBlanket(f, name, arity, impl, trait, pos, t, blanketPath, blanketTypeParam) and
2050+
functionInfoBlanketLike(f, name, arity, impl, trait, pos, t, blanketPath, blanketTypeParam) and
19562051
(
19572052
if pos.isReturn()
19582053
then
19592054
// We only check that the context of the call provides relevant type information
19602055
// when no argument can
19612056
not exists(FunctionPosition pos0 |
1962-
functionInfoBlanket(f, name, arity, impl, trait, pos0, _, _, _) and
2057+
functionInfoBlanketLike(f, name, arity, impl, trait, pos0, _, _, _) and
19632058
not pos0.isReturn()
19642059
)
19652060
else any()
19662061
)
19672062
}
19682063

19692064
pragma[nomagic]
1970-
private predicate blanketCallTraitCandidate(Element fc, Trait trait) {
2065+
private predicate blanketLikeCallTraitCandidate(Element fc, Trait trait) {
19712066
exists(string name, int arity |
19722067
fc.(NonMethodCall).hasNameAndArity(name, arity) and
1973-
functionInfoBlanketRelevantPos(_, name, arity, _, trait, _, _, _, _)
2068+
functionInfoBlanketLikeRelevantPos(_, name, arity, _, trait, _, _, _, _)
19742069
|
19752070
not fc.(Call).hasTrait()
19762071
or
19772072
trait = fc.(Call).getTrait()
19782073
)
19792074
}
19802075

1981-
private module BlanketTraitIsVisible = TraitIsVisible<blanketCallTraitCandidate/2>;
2076+
private module BlanketTraitIsVisible = TraitIsVisible<blanketLikeCallTraitCandidate/2>;
19822077

19832078
/** A (potential) non-method call, `f(x)`. */
19842079
final class NonMethodCall extends CallExpr {
@@ -2037,13 +2132,13 @@ private module NonMethodResolution {
20372132
}
20382133

20392134
pragma[nomagic]
2040-
predicate resolveCallTargetBlanketCandidate(
2135+
predicate resolveCallTargetBlanketLikeCandidate(
20412136
ImplItemNode impl, FunctionPosition pos, TypePath blanketPath, TypeParam blanketTypeParam
20422137
) {
20432138
exists(string name, int arity, Trait trait, AssocFunctionType t |
20442139
this.hasNameAndArity(name, arity) and
20452140
exists(this.getTypeAt(pos, blanketPath)) and
2046-
functionInfoBlanketRelevantPos(_, name, arity, impl, trait, pos, t, blanketPath,
2141+
functionInfoBlanketLikeRelevantPos(_, name, arity, impl, trait, pos, t, blanketPath,
20472142
blanketTypeParam) and
20482143
BlanketTraitIsVisible::traitIsVisible(this, trait)
20492144
)
@@ -2080,7 +2175,7 @@ private module NonMethodResolution {
20802175

20812176
private newtype TCallAndBlanketPos =
20822177
MkCallAndBlanketPos(NonMethodCall fc, FunctionPosition pos) {
2083-
fc.resolveCallTargetBlanketCandidate(_, pos, _, _)
2178+
fc.resolveCallTargetBlanketLikeCandidate(_, pos, _, _)
20842179
}
20852180

20862181
/** A call tagged with a position. */
@@ -2106,7 +2201,7 @@ private module NonMethodResolution {
21062201
) {
21072202
exists(NonMethodCall fc, FunctionPosition pos |
21082203
fcp = MkCallAndBlanketPos(fc, pos) and
2109-
fc.resolveCallTargetBlanketCandidate(impl, pos, blanketPath, blanketTypeParam)
2204+
fc.resolveCallTargetBlanketLikeCandidate(impl, pos, blanketPath, blanketTypeParam)
21102205
)
21112206
}
21122207
}
@@ -2129,12 +2224,12 @@ private module NonMethodResolution {
21292224
exists(FunctionPosition pos |
21302225
ArgSatisfiesBlanketConstraint::satisfiesBlanketConstraint(fcp, abs) and
21312226
fcp = MkCallAndBlanketPos(_, pos) and
2132-
functionInfoBlanketRelevantPos(_, _, _, abs, _, pos, constraint, _, _)
2227+
functionInfoBlanketLikeRelevantPos(_, _, _, abs, _, pos, constraint, _, _)
21332228
)
21342229
}
21352230

21362231
predicate relevantConstraint(AssocFunctionType constraint) {
2137-
functionInfoBlanketRelevantPos(_, _, _, _, _, _, constraint, _, _)
2232+
functionInfoBlanketLikeRelevantPos(_, _, _, _, _, _, constraint, _, _)
21382233
}
21392234
}
21402235

0 commit comments

Comments
 (0)