Skip to content

Commit 7cc6289

Browse files
committed
Rust: Compute incompatible blanket implementations
1 parent 0e36a10 commit 7cc6289

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
@@ -859,14 +859,14 @@ private predicate assocFunctionInfo(
859859

860860
/**
861861
* Holds if function `f` with the name `name` and the arity `arity` exists in
862-
* blanket implementation `impl` of `trait`, and the type at position
862+
* blanket (like) implementation `impl` of `trait`, and the type at position
863863
* `pos` is `t`.
864864
*
865865
* `blanketPath` points to the type `blanketTypeParam` inside `t`, which
866866
* is the type parameter used in the blanket implementation.
867867
*/
868868
pragma[nomagic]
869-
private predicate functionInfoBlanket(
869+
private predicate functionInfoBlanketLike(
870870
Function f, string name, int arity, ImplItemNode impl, Trait trait, FunctionTypePosition pos,
871871
AssocFunctionType t, TypePath blanketPath, TypeParam blanketTypeParam
872872
) {
@@ -985,19 +985,20 @@ private module MethodResolution {
985985

986986
/**
987987
* Holds if method `m` with the name `name` and the arity `arity` exists in
988-
* blanket implementation `impl` of `trait`, and the type of the `self`
988+
* blanket (like) implementation `impl` of `trait`, and the type of the `self`
989989
* parameter is `selfType`.
990990
*
991991
* `blanketPath` points to the type `blanketTypeParam` inside `selfType`, which
992992
* is the type parameter used in the blanket implementation.
993993
*/
994994
pragma[nomagic]
995-
private predicate methodInfoBlanket(
995+
private predicate methodInfoBlanketLike(
996996
Method m, string name, int arity, ImplItemNode impl, Trait trait, AssocFunctionType selfType,
997997
TypePath blanketPath, TypeParam blanketTypeParam
998998
) {
999999
exists(FunctionTypePosition pos |
1000-
functionInfoBlanket(m, name, arity, impl, trait, pos, selfType, blanketPath, blanketTypeParam) and
1000+
functionInfoBlanketLike(m, name, arity, impl, trait, pos, selfType, blanketPath,
1001+
blanketTypeParam) and
10011002
pos.isSelf()
10021003
)
10031004
}
@@ -1071,8 +1072,8 @@ private module MethodResolution {
10711072
}
10721073

10731074
/**
1074-
* Holds if method call `mc` may target a method in blanket implementation `i`
1075-
* with `self` parameter having type `selfType`.
1075+
* Holds if method call `mc` may target a method in blanket (like) implementation
1076+
* `impl` with `self` parameter having type `selfType`.
10761077
*
10771078
* `blanketPath` points to the type `blanketTypeParam` inside `selfType`, which
10781079
* is the type parameter used in the blanket implementation.
@@ -1083,13 +1084,13 @@ private module MethodResolution {
10831084
*/
10841085
bindingset[mc]
10851086
pragma[inline_late]
1086-
private predicate methodCallBlanketCandidate(
1087+
private predicate methodCallBlanketLikeCandidate(
10871088
MethodCall mc, Method m, ImplItemNode impl, AssocFunctionType self, TypePath blanketPath,
10881089
TypeParam blanketTypeParam
10891090
) {
10901091
exists(string name, int arity |
10911092
mc.hasNameAndArity(name, arity) and
1092-
methodInfoBlanket(m, name, arity, impl, _, self, blanketPath, blanketTypeParam)
1093+
methodInfoBlanketLike(m, name, arity, impl, _, self, blanketPath, blanketTypeParam)
10931094
|
10941095
methodCallVisibleImplTraitCandidate(mc, impl)
10951096
or
@@ -1145,7 +1146,7 @@ private module MethodResolution {
11451146
or
11461147
this.supportsAutoDerefAndBorrow() and
11471148
exists(TypePath path0, Type t0, string derefChain0 |
1148-
this.hasNoCompatibleTarget(derefChain0, _) and
1149+
this.hasNoCompatibleTargetBorrow(derefChain0, _) and
11491150
t0 = this.getACandidateReceiverTypeAtNoBorrow(path0, derefChain0)
11501151
|
11511152
path0.isCons(TRefTypeParameter(), path) and
@@ -1174,6 +1175,21 @@ private module MethodResolution {
11741175
derefChainBorrow = ";"
11751176
}
11761177

1178+
/**
1179+
* Holds if the method inside blanket-like implementation `i` with matching name
1180+
* and arity can be ruled out as a target of this call, either because the candidate
1181+
* receiver type represented by `derefChainBorrow` is incompatible with the `self`
1182+
* parameter type, or because the blanket constraint is not satisfied.
1183+
*/
1184+
pragma[nomagic]
1185+
private predicate hasIncompatibleBlanketLikeTarget(ImplItemNode i, string derefChainBorrow) {
1186+
ReceiverIsNotInstantiationOfBlanketLikeSelfParam::argIsNotInstantiationOf(MkMethodCallCand(this,
1187+
derefChainBorrow), i, _)
1188+
or
1189+
ReceiverSatisfiesBlanketLikeConstraint::satisfiesNotBlanketConstraint(MkMethodCallCand(this,
1190+
derefChainBorrow), i)
1191+
}
1192+
11771193
/**
11781194
* Same as `getACandidateReceiverTypeAt`, but with traits substituted in for types
11791195
* with trait bounds.
@@ -1191,10 +1207,9 @@ private module MethodResolution {
11911207
}
11921208

11931209
bindingset[strippedTypePath, strippedType, derefChainBorrow]
1194-
private predicate hasNoCompatibleTargetCheck(
1210+
private predicate hasNoCompatibleNonBlanketLikeTargetCheck(
11951211
TypePath strippedTypePath, Type strippedType, string derefChainBorrow
11961212
) {
1197-
// todo: also check that all blanket implementation candidates are incompatible
11981213
forall(ImplOrTraitItemNode i |
11991214
methodCallNonBlanketCandidate(this, _, i, _, strippedTypePath, strippedType)
12001215
or
@@ -1204,6 +1219,28 @@ private module MethodResolution {
12041219
)
12051220
}
12061221

1222+
bindingset[strippedTypePath, strippedType, derefChainBorrow]
1223+
private predicate hasNoCompatibleTargetCheck(
1224+
TypePath strippedTypePath, Type strippedType, string derefChainBorrow
1225+
) {
1226+
this.hasNoCompatibleNonBlanketLikeTargetCheck(strippedTypePath, strippedType, derefChainBorrow) and
1227+
forall(ImplItemNode i | methodCallBlanketLikeCandidate(this, _, i, _, _, _) |
1228+
this.hasIncompatibleBlanketLikeTarget(i, derefChainBorrow)
1229+
)
1230+
}
1231+
1232+
bindingset[strippedTypePath, strippedType, derefChainBorrow]
1233+
private predicate hasNoCompatibleNonBlanketTargetCheck(
1234+
TypePath strippedTypePath, Type strippedType, string derefChainBorrow
1235+
) {
1236+
this.hasNoCompatibleNonBlanketLikeTargetCheck(strippedTypePath, strippedType, derefChainBorrow) and
1237+
forall(ImplItemNode i |
1238+
methodCallBlanketLikeCandidate(this, _, i, _, _, _) and not i.isBlanketImplementation()
1239+
|
1240+
this.hasIncompatibleBlanketLikeTarget(i, derefChainBorrow)
1241+
)
1242+
}
1243+
12071244
/**
12081245
* Holds if the candidate receiver type represented by
12091246
* `derefChainBorrow = derefChain;` does not have a matching method target.
@@ -1214,7 +1251,7 @@ private module MethodResolution {
12141251
this.supportsAutoDerefAndBorrow()
12151252
or
12161253
// needed for the `hasNoCompatibleTarget` check in
1217-
// `SatisfiesBlanketConstraintInput::hasBlanketCandidate`
1254+
// `ReceiverSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate`
12181255
derefChain = ""
12191256
) and
12201257
exists(TypePath strippedTypePath, Type strippedType |
@@ -1225,13 +1262,35 @@ private module MethodResolution {
12251262
)
12261263
}
12271264

1265+
/**
1266+
* Holds if the candidate receiver type represented by
1267+
* `derefChainBorrow = derefChain;` does not have a matching non-blanket
1268+
* method target.
1269+
*/
1270+
pragma[nomagic]
1271+
predicate hasNoCompatibleNonBlanketTargetNoBorrow(string derefChain, string derefChainBorrow) {
1272+
(
1273+
this.supportsAutoDerefAndBorrow()
1274+
or
1275+
// needed for the `hasNoCompatibleTarget` check in
1276+
// `ReceiverSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate`
1277+
derefChain = ""
1278+
) and
1279+
exists(TypePath strippedTypePath, Type strippedType |
1280+
derefChainBorrow = derefChain + ";" and
1281+
not derefChain.matches("%.ref") and // no need to try a borrow if the last thing we did was a deref
1282+
strippedType = this.getComplexStrippedType(strippedTypePath, derefChainBorrow) and
1283+
this.hasNoCompatibleNonBlanketTargetCheck(strippedTypePath, strippedType, derefChainBorrow)
1284+
)
1285+
}
1286+
12281287
/**
12291288
* Holds if the candidate receiver type represented by
12301289
* `derefChainBorrow = derefChain;borrow` does not have a matching method
12311290
* target.
12321291
*/
12331292
pragma[nomagic]
1234-
predicate hasNoCompatibleTarget(string derefChain, string derefChainBorrow) {
1293+
predicate hasNoCompatibleTargetBorrow(string derefChain, string derefChainBorrow) {
12351294
exists(TypePath strippedTypePath, Type strippedType |
12361295
derefChainBorrow = derefChain + ";borrow" and
12371296
this.hasNoCompatibleTargetNoBorrow(derefChain, _) and
@@ -1240,6 +1299,21 @@ private module MethodResolution {
12401299
)
12411300
}
12421301

1302+
/**
1303+
* Holds if the candidate receiver type represented by
1304+
* `derefChainBorrow = derefChain;borrow` does not have a matching non-blanket
1305+
* method target.
1306+
*/
1307+
pragma[nomagic]
1308+
predicate hasNoCompatibleNonBlanketTargetBorrow(string derefChain, string derefChainBorrow) {
1309+
exists(TypePath strippedTypePath, Type strippedType |
1310+
derefChainBorrow = derefChain + ";borrow" and
1311+
this.hasNoCompatibleTargetNoBorrow(derefChain, _) and
1312+
strippedType = this.getComplexStrippedType(strippedTypePath, derefChainBorrow) and
1313+
this.hasNoCompatibleNonBlanketTargetCheck(strippedTypePath, strippedType, derefChainBorrow)
1314+
)
1315+
}
1316+
12431317
/**
12441318
* Gets a [candidate receiver type][1] of this method call at `path`.
12451319
*
@@ -1432,10 +1506,10 @@ private module MethodResolution {
14321506
}
14331507

14341508
pragma[nomagic]
1435-
predicate hasNoCompatibleTarget() {
1436-
mc_.hasNoCompatibleTarget(_, derefChainBorrow)
1509+
predicate hasNoCompatibleNonBlanketTarget() {
1510+
mc_.hasNoCompatibleNonBlanketTargetBorrow(_, derefChainBorrow)
14371511
or
1438-
mc_.hasNoCompatibleTargetNoBorrow(_, derefChainBorrow)
1512+
mc_.hasNoCompatibleNonBlanketTargetNoBorrow(_, derefChainBorrow)
14391513
}
14401514

14411515
pragma[nomagic]
@@ -1518,20 +1592,20 @@ private module MethodResolution {
15181592
Location getLocation() { result = mc_.getLocation() }
15191593
}
15201594

1521-
private module ReceiverSatisfiesBlanketConstraintInput implements
1595+
private module ReceiverSatisfiesBlanketLikeConstraintInput implements
15221596
BlanketImplementation::SatisfiesBlanketConstraintInputSig<MethodCallCand>
15231597
{
15241598
pragma[nomagic]
15251599
predicate hasBlanketCandidate(
15261600
MethodCallCand mcc, ImplItemNode impl, TypePath blanketPath, TypeParam blanketTypeParam
15271601
) {
1528-
exists(MethodCall mc, string name, int arity |
1529-
mcc.hasSignature(mc, _, _, name, arity) and
1530-
methodCallBlanketCandidate(mc, _, impl, _, blanketPath, blanketTypeParam) and
1602+
exists(MethodCall mc |
1603+
mc = mcc.getMethodCall() and
1604+
methodCallBlanketLikeCandidate(mc, _, impl, _, blanketPath, blanketTypeParam) and
15311605
// Only apply blanket implementations when no other implementations are possible;
15321606
// this is to account for codebases that use the (unstable) specialization feature
15331607
// (https://rust-lang.github.io/rfcs/1210-impl-specialization.html)
1534-
mcc.hasNoCompatibleTarget()
1608+
(mcc.hasNoCompatibleNonBlanketTarget() or not impl.isBlanketImplementation())
15351609
|
15361610
mcc.hasNoBorrow()
15371611
or
@@ -1540,9 +1614,9 @@ private module MethodResolution {
15401614
}
15411615
}
15421616

1543-
private module ReceiverSatisfiesBlanketConstraint =
1617+
private module ReceiverSatisfiesBlanketLikeConstraint =
15441618
BlanketImplementation::SatisfiesBlanketConstraint<MethodCallCand,
1545-
ReceiverSatisfiesBlanketConstraintInput>;
1619+
ReceiverSatisfiesBlanketLikeConstraintInput>;
15461620

15471621
/**
15481622
* A configuration for matching the type of a receiver against the type of
@@ -1563,8 +1637,8 @@ private module MethodResolution {
15631637
|
15641638
methodCallNonBlanketCandidate(mc, m, i, selfType, strippedTypePath, strippedType)
15651639
or
1566-
methodCallBlanketCandidate(mc, m, i, selfType, _, _) and
1567-
ReceiverSatisfiesBlanketConstraint::satisfiesBlanketConstraint(mcc, i)
1640+
methodCallBlanketLikeCandidate(mc, m, i, selfType, _, _) and
1641+
ReceiverSatisfiesBlanketLikeConstraint::satisfiesBlanketConstraint(mcc, i)
15681642
)
15691643
}
15701644

@@ -1589,6 +1663,30 @@ private module MethodResolution {
15891663
private module ReceiverIsInstantiationOfSelfParam =
15901664
ArgIsInstantiationOf<MethodCallCand, ReceiverIsInstantiationOfSelfParamInput>;
15911665

1666+
/**
1667+
* A configuration for anti-matching the type of a receiver against the type of
1668+
* a `self` parameter belonging to a blanket (like) implementation.
1669+
*/
1670+
private module ReceiverIsNotInstantiationOfBlanketLikeSelfParamInput implements
1671+
IsInstantiationOfInputSig<MethodCallCand, AssocFunctionType>
1672+
{
1673+
pragma[nomagic]
1674+
predicate potentialInstantiationOf(
1675+
MethodCallCand mcc, TypeAbstraction abs, AssocFunctionType constraint
1676+
) {
1677+
methodCallBlanketLikeCandidate(mcc.getMethodCall(), _, abs, constraint, _, _) and
1678+
if abs.(Impl).hasTrait()
1679+
then
1680+
// inherent methods take precedence over trait methods, so only allow
1681+
// trait methods when there are no matching inherent methods
1682+
mcc.hasNoInherentTarget()
1683+
else any()
1684+
}
1685+
}
1686+
1687+
private module ReceiverIsNotInstantiationOfBlanketLikeSelfParam =
1688+
ArgIsInstantiationOf<MethodCallCand, ReceiverIsNotInstantiationOfBlanketLikeSelfParamInput>;
1689+
15921690
/**
15931691
* A configuration for matching the type qualifier of a method call
15941692
* against the type being implemented in an `impl` block. For example,
@@ -1642,10 +1740,6 @@ private module MethodResolution {
16421740
ReceiverIsInstantiationOfSelfParamInput::potentialInstantiationOf0(mcc, abs, constraint) and
16431741
abs = any(Impl i | not i.hasTrait())
16441742
}
1645-
1646-
predicate relevantConstraint(AssocFunctionType constraint) {
1647-
methodInfo(_, _, _, _, constraint, _, _)
1648-
}
16491743
}
16501744

16511745
private module ReceiverIsNotInstantiationOfInherentSelfParam =
@@ -1901,18 +1995,18 @@ private module NonMethodResolution {
19011995
}
19021996

19031997
pragma[nomagic]
1904-
private predicate functionInfoBlanketRelevantPos(
1998+
private predicate functionInfoBlanketLikeRelevantPos(
19051999
NonMethodFunction f, string name, int arity, ImplItemNode impl, Trait trait,
19062000
FunctionTypePosition pos, AssocFunctionType t, TypePath blanketPath, TypeParam blanketTypeParam
19072001
) {
1908-
functionInfoBlanket(f, name, arity, impl, trait, pos, t, blanketPath, blanketTypeParam) and
2002+
functionInfoBlanketLike(f, name, arity, impl, trait, pos, t, blanketPath, blanketTypeParam) and
19092003
(
19102004
not pos.isReturn()
19112005
or
19122006
// We only check that the context of the call provides relevant type information
19132007
// when no argument can
19142008
not exists(FunctionTypePosition pos0 |
1915-
functionInfoBlanket(f, name, arity, impl, trait, pos0, _, _, _) and
2009+
functionInfoBlanketLike(f, name, arity, impl, trait, pos0, _, _, _) and
19162010
not pos0.isReturn()
19172011
)
19182012
)
@@ -1922,7 +2016,7 @@ private module NonMethodResolution {
19222016
private predicate blanketCallTraitCandidate(Element fc, Trait trait) {
19232017
exists(string name, int arity |
19242018
fc.(NonMethodCall).hasNameAndArity(name, arity) and
1925-
functionInfoBlanketRelevantPos(_, name, arity, _, trait, _, _, _, _)
2019+
functionInfoBlanketLikeRelevantPos(_, name, arity, _, trait, _, _, _, _)
19262020
|
19272021
not fc.(Call).hasTrait()
19282022
or
@@ -1998,7 +2092,7 @@ private module NonMethodResolution {
19982092
exists(string name, int arity, Trait trait, AssocFunctionType t |
19992093
this.hasNameAndArity(name, arity) and
20002094
exists(this.getTypeAt(pos, blanketPath)) and
2001-
functionInfoBlanketRelevantPos(_, name, arity, impl, trait, pos, t, blanketPath,
2095+
functionInfoBlanketLikeRelevantPos(_, name, arity, impl, trait, pos, t, blanketPath,
20022096
blanketTypeParam) and
20032097
BlanketTraitIsVisible::traitIsVisible(this, trait)
20042098
)
@@ -2084,12 +2178,12 @@ private module NonMethodResolution {
20842178
exists(FunctionTypePosition pos |
20852179
ArgSatisfiesBlanketConstraint::satisfiesBlanketConstraint(fcp, abs) and
20862180
fcp = MkCallAndBlanketPos(_, pos) and
2087-
functionInfoBlanketRelevantPos(_, _, _, abs, _, pos, constraint, _, _)
2181+
functionInfoBlanketLikeRelevantPos(_, _, _, abs, _, pos, constraint, _, _)
20882182
)
20892183
}
20902184

20912185
predicate relevantConstraint(AssocFunctionType constraint) {
2092-
functionInfoBlanketRelevantPos(_, _, _, _, _, _, constraint, _, _)
2186+
functionInfoBlanketLikeRelevantPos(_, _, _, _, _, _, constraint, _, _)
20932187
}
20942188
}
20952189

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)