Skip to content

Commit 9976877

Browse files
committed
Rust: Compute incompatible blanket implementations
1 parent 877cbb7 commit 9976877

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
@@ -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, FunctionPosition 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(FunctionPosition 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
@@ -1219,6 +1220,23 @@ private module MethodResolution {
12191220
borrow), i, _)
12201221
}
12211222

1223+
/**
1224+
* Holds if the method inside blanket-like implementation `impl` 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 `derefChain` and `borrow` is incompatible with the `self`
1227+
* parameter type, or because the blanket constraint is not satisfied.
1228+
*/
1229+
pragma[nomagic]
1230+
private predicate hasIncompatibleBlanketLikeTarget(
1231+
ImplItemNode impl, string derefChain, boolean borrow
1232+
) {
1233+
ReceiverIsNotInstantiationOfBlanketLikeSelfParam::argIsNotInstantiationOf(MkMethodCallCand(this,
1234+
derefChain, borrow), impl, _)
1235+
or
1236+
ReceiverSatisfiesBlanketLikeConstraint::satisfiesNotBlanketConstraint(MkMethodCallCand(this,
1237+
derefChain, borrow), impl)
1238+
}
1239+
12221240
/**
12231241
* Same as `getACandidateReceiverTypeAt`, but with traits substituted in for types
12241242
* with trait bounds.
@@ -1237,18 +1255,41 @@ private module MethodResolution {
12371255
isComplexRootStripped(strippedTypePath, result)
12381256
}
12391257

1240-
bindingset[strippedTypePath, strippedType, derefChain, borrow]
1241-
private predicate hasNoCompatibleTargetCheck(
1258+
bindingset[derefChain, borrow, strippedTypePath, strippedType]
1259+
private predicate hasNoCompatibleNonBlanketLikeTargetCheck(
12421260
string derefChain, boolean borrow, TypePath strippedTypePath, Type strippedType
12431261
) {
1244-
// todo: also check that all blanket implementation candidates are incompatible
12451262
forall(ImplOrTraitItemNode i |
12461263
methodCallNonBlanketCandidate(this, _, i, _, strippedTypePath, strippedType)
12471264
|
12481265
this.hasIncompatibleTarget(i, derefChain, borrow)
12491266
)
12501267
}
12511268

1269+
bindingset[derefChain, borrow, strippedTypePath, strippedType]
1270+
private predicate hasNoCompatibleTargetCheck(
1271+
string derefChain, boolean borrow, TypePath strippedTypePath, Type strippedType
1272+
) {
1273+
this.hasNoCompatibleNonBlanketLikeTargetCheck(derefChain, borrow, strippedTypePath,
1274+
strippedType) and
1275+
forall(ImplItemNode i | methodCallBlanketLikeCandidate(this, _, i, _, _, _) |
1276+
this.hasIncompatibleBlanketLikeTarget(i, derefChain, borrow)
1277+
)
1278+
}
1279+
1280+
bindingset[derefChain, borrow, strippedTypePath, strippedType]
1281+
private predicate hasNoCompatibleNonBlanketTargetCheck(
1282+
string derefChain, boolean borrow, TypePath strippedTypePath, Type strippedType
1283+
) {
1284+
this.hasNoCompatibleNonBlanketLikeTargetCheck(derefChain, borrow, strippedTypePath,
1285+
strippedType) and
1286+
forall(ImplItemNode i |
1287+
methodCallBlanketLikeCandidate(this, _, i, _, _, _) and not i.isBlanketImplementation()
1288+
|
1289+
this.hasIncompatibleBlanketLikeTarget(i, derefChain, borrow)
1290+
)
1291+
}
1292+
12521293
/**
12531294
* Holds if the candidate receiver type represented by `derefChain` does not
12541295
* have a matching method target.
@@ -1259,7 +1300,7 @@ private module MethodResolution {
12591300
this.supportsAutoDerefAndBorrow()
12601301
or
12611302
// needed for the `hasNoCompatibleTarget` check in
1262-
// `SatisfiesBlanketConstraintInput::hasBlanketCandidate`
1303+
// `ReceiverSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate`
12631304
derefChain = ""
12641305
) and
12651306
exists(TypePath strippedTypePath, Type strippedType |
@@ -1269,6 +1310,26 @@ private module MethodResolution {
12691310
)
12701311
}
12711312

1313+
/**
1314+
* Holds if the candidate receiver type represented by `derefChain` does not have
1315+
* a matching non-blanket method target.
1316+
*/
1317+
pragma[nomagic]
1318+
predicate hasNoCompatibleNonBlanketTargetNoBorrow(string derefChain) {
1319+
(
1320+
this.supportsAutoDerefAndBorrow()
1321+
or
1322+
// needed for the `hasNoCompatibleTarget` check in
1323+
// `ReceiverSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate`
1324+
derefChain = ""
1325+
) and
1326+
exists(TypePath strippedTypePath, Type strippedType |
1327+
not derefChain.matches("%.ref") and // no need to try a borrow if the last thing we did was a deref
1328+
strippedType = this.getComplexStrippedType(derefChain, false, strippedTypePath) and
1329+
this.hasNoCompatibleNonBlanketTargetCheck(derefChain, false, strippedTypePath, strippedType)
1330+
)
1331+
}
1332+
12721333
/**
12731334
* Holds if the candidate receiver type represented by `derefChain`, followed
12741335
* by a borrow, does not have a matching method target.
@@ -1278,7 +1339,21 @@ private module MethodResolution {
12781339
exists(TypePath strippedTypePath, Type strippedType |
12791340
this.hasNoCompatibleTargetNoBorrow(derefChain) and
12801341
strippedType = this.getComplexStrippedType(derefChain, true, strippedTypePath) and
1281-
this.hasNoCompatibleTargetCheck(derefChain, true, strippedTypePath, strippedType)
1342+
this.hasNoCompatibleNonBlanketLikeTargetCheck(derefChain, true, strippedTypePath,
1343+
strippedType)
1344+
)
1345+
}
1346+
1347+
/**
1348+
* Holds if the candidate receiver type represented by `derefChain`, followed
1349+
* by a borrow, does not have a matching non-blanket method target.
1350+
*/
1351+
pragma[nomagic]
1352+
predicate hasNoCompatibleNonBlanketTargetBorrow(string derefChain) {
1353+
exists(TypePath strippedTypePath, Type strippedType |
1354+
this.hasNoCompatibleTargetNoBorrow(derefChain) and
1355+
strippedType = this.getComplexStrippedType(derefChain, true, strippedTypePath) and
1356+
this.hasNoCompatibleNonBlanketTargetCheck(derefChain, true, strippedTypePath, strippedType)
12821357
)
12831358
}
12841359

@@ -1473,11 +1548,11 @@ private module MethodResolution {
14731548
}
14741549

14751550
pragma[nomagic]
1476-
predicate hasNoCompatibleTarget() {
1477-
mc_.hasNoCompatibleTargetBorrow(derefChain) and
1551+
predicate hasNoCompatibleNonBlanketTarget() {
1552+
mc_.hasNoCompatibleNonBlanketTargetBorrow(derefChain) and
14781553
borrow = true
14791554
or
1480-
mc_.hasNoCompatibleTargetNoBorrow(derefChain) and
1555+
mc_.hasNoCompatibleNonBlanketTargetNoBorrow(derefChain) and
14811556
borrow = false
14821557
}
14831558

@@ -1558,20 +1633,20 @@ private module MethodResolution {
15581633
Location getLocation() { result = mc_.getLocation() }
15591634
}
15601635

1561-
private module ReceiverSatisfiesBlanketConstraintInput implements
1636+
private module ReceiverSatisfiesBlanketLikeConstraintInput implements
15621637
BlanketImplementation::SatisfiesBlanketConstraintInputSig<MethodCallCand>
15631638
{
15641639
pragma[nomagic]
15651640
predicate hasBlanketCandidate(
15661641
MethodCallCand mcc, ImplItemNode impl, TypePath blanketPath, TypeParam blanketTypeParam
15671642
) {
1568-
exists(MethodCall mc, string name, int arity |
1569-
mcc.hasSignature(mc, _, _, name, arity) and
1570-
methodCallBlanketCandidate(mc, _, impl, _, blanketPath, blanketTypeParam) and
1643+
exists(MethodCall mc |
1644+
mc = mcc.getMethodCall() and
1645+
methodCallBlanketLikeCandidate(mc, _, impl, _, blanketPath, blanketTypeParam) and
15711646
// Only apply blanket implementations when no other implementations are possible;
15721647
// this is to account for codebases that use the (unstable) specialization feature
15731648
// (https://rust-lang.github.io/rfcs/1210-impl-specialization.html)
1574-
mcc.hasNoCompatibleTarget()
1649+
(mcc.hasNoCompatibleNonBlanketTarget() or not impl.isBlanketImplementation())
15751650
|
15761651
mcc.hasNoBorrow()
15771652
or
@@ -1580,9 +1655,9 @@ private module MethodResolution {
15801655
}
15811656
}
15821657

1583-
private module ReceiverSatisfiesBlanketConstraint =
1658+
private module ReceiverSatisfiesBlanketLikeConstraint =
15841659
BlanketImplementation::SatisfiesBlanketConstraint<MethodCallCand,
1585-
ReceiverSatisfiesBlanketConstraintInput>;
1660+
ReceiverSatisfiesBlanketLikeConstraintInput>;
15861661

15871662
/**
15881663
* A configuration for matching the type of a receiver against the type of
@@ -1603,8 +1678,8 @@ private module MethodResolution {
16031678
|
16041679
methodCallNonBlanketCandidate(mc, m, i, selfType, strippedTypePath, strippedType)
16051680
or
1606-
methodCallBlanketCandidate(mc, m, i, selfType, _, _) and
1607-
ReceiverSatisfiesBlanketConstraint::satisfiesBlanketConstraint(mcc, i)
1681+
methodCallBlanketLikeCandidate(mc, m, i, selfType, _, _) and
1682+
ReceiverSatisfiesBlanketLikeConstraint::satisfiesBlanketConstraint(mcc, i)
16081683
)
16091684
}
16101685

@@ -1629,6 +1704,30 @@ private module MethodResolution {
16291704
private module ReceiverIsInstantiationOfSelfParam =
16301705
ArgIsInstantiationOf<MethodCallCand, ReceiverIsInstantiationOfSelfParamInput>;
16311706

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

16911786
private module ReceiverIsNotInstantiationOfInherentSelfParam =
@@ -1951,37 +2046,37 @@ private module NonMethodResolution {
19512046
}
19522047

19532048
pragma[nomagic]
1954-
private predicate functionInfoBlanketRelevantPos(
2049+
private predicate functionInfoBlanketLikeRelevantPos(
19552050
NonMethodFunction f, string name, int arity, ImplItemNode impl, Trait trait,
19562051
FunctionPosition pos, AssocFunctionType t, TypePath blanketPath, TypeParam blanketTypeParam
19572052
) {
1958-
functionInfoBlanket(f, name, arity, impl, trait, pos, t, blanketPath, blanketTypeParam) and
2053+
functionInfoBlanketLike(f, name, arity, impl, trait, pos, t, blanketPath, blanketTypeParam) and
19592054
(
19602055
if pos.isReturn()
19612056
then
19622057
// We only check that the context of the call provides relevant type information
19632058
// when no argument can
19642059
not exists(FunctionPosition pos0 |
1965-
functionInfoBlanket(f, name, arity, impl, trait, pos0, _, _, _) and
2060+
functionInfoBlanketLike(f, name, arity, impl, trait, pos0, _, _, _) and
19662061
not pos0.isReturn()
19672062
)
19682063
else any()
19692064
)
19702065
}
19712066

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

1984-
private module BlanketTraitIsVisible = TraitIsVisible<blanketCallTraitCandidate/2>;
2079+
private module BlanketTraitIsVisible = TraitIsVisible<blanketLikeCallTraitCandidate/2>;
19852080

19862081
/** A (potential) non-method call, `f(x)`. */
19872082
final class NonMethodCall extends CallExpr {
@@ -2040,13 +2135,13 @@ private module NonMethodResolution {
20402135
}
20412136

20422137
pragma[nomagic]
2043-
predicate resolveCallTargetBlanketCandidate(
2138+
predicate resolveCallTargetBlanketLikeCandidate(
20442139
ImplItemNode impl, FunctionPosition pos, TypePath blanketPath, TypeParam blanketTypeParam
20452140
) {
20462141
exists(string name, int arity, Trait trait, AssocFunctionType t |
20472142
this.hasNameAndArity(name, arity) and
20482143
exists(this.getTypeAt(pos, blanketPath)) and
2049-
functionInfoBlanketRelevantPos(_, name, arity, impl, trait, pos, t, blanketPath,
2144+
functionInfoBlanketLikeRelevantPos(_, name, arity, impl, trait, pos, t, blanketPath,
20502145
blanketTypeParam) and
20512146
BlanketTraitIsVisible::traitIsVisible(this, trait)
20522147
)
@@ -2083,7 +2178,7 @@ private module NonMethodResolution {
20832178

20842179
private newtype TCallAndBlanketPos =
20852180
MkCallAndBlanketPos(NonMethodCall fc, FunctionPosition pos) {
2086-
fc.resolveCallTargetBlanketCandidate(_, pos, _, _)
2181+
fc.resolveCallTargetBlanketLikeCandidate(_, pos, _, _)
20872182
}
20882183

20892184
/** A call tagged with a position. */
@@ -2109,7 +2204,7 @@ private module NonMethodResolution {
21092204
) {
21102205
exists(NonMethodCall fc, FunctionPosition pos |
21112206
fcp = MkCallAndBlanketPos(fc, pos) and
2112-
fc.resolveCallTargetBlanketCandidate(impl, pos, blanketPath, blanketTypeParam)
2207+
fc.resolveCallTargetBlanketLikeCandidate(impl, pos, blanketPath, blanketTypeParam)
21132208
)
21142209
}
21152210
}
@@ -2132,12 +2227,12 @@ private module NonMethodResolution {
21322227
exists(FunctionPosition pos |
21332228
ArgSatisfiesBlanketConstraint::satisfiesBlanketConstraint(fcp, abs) and
21342229
fcp = MkCallAndBlanketPos(_, pos) and
2135-
functionInfoBlanketRelevantPos(_, _, _, abs, _, pos, constraint, _, _)
2230+
functionInfoBlanketLikeRelevantPos(_, _, _, abs, _, pos, constraint, _, _)
21362231
)
21372232
}
21382233

21392234
predicate relevantConstraint(AssocFunctionType constraint) {
2140-
functionInfoBlanketRelevantPos(_, _, _, _, _, _, constraint, _, _)
2235+
functionInfoBlanketLikeRelevantPos(_, _, _, _, _, _, constraint, _, _)
21412236
}
21422237
}
21432238

0 commit comments

Comments
 (0)