Skip to content

Commit 0fa2715

Browse files
committed
Rust: Fix candidate receiver type calculation for trait bounds
1 parent 0d718a2 commit 0fa2715

File tree

5 files changed

+116
-41
lines changed

5 files changed

+116
-41
lines changed

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

Lines changed: 87 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,20 +1471,18 @@ private module MethodResolution {
14711471
}
14721472

14731473
/**
1474-
* Same as `getACandidateReceiverTypeAt`, but with traits substituted in for types
1475-
* with trait bounds.
1474+
* Same as `getACandidateReceiverTypeAt`, but excludes pseudo types `!` and `unknown`.
14761475
*/
14771476
pragma[nomagic]
1478-
Type getACandidateReceiverTypeAtSubstituteLookupTraits(
1479-
string derefChain, boolean borrow, TypePath path
1480-
) {
1481-
result = substituteLookupTraits(this.getACandidateReceiverTypeAt(derefChain, borrow, path))
1477+
Type getANonPseudoCandidateReceiverTypeAt(string derefChain, boolean borrow, TypePath path) {
1478+
result = this.getACandidateReceiverTypeAt(derefChain, borrow, path) and
1479+
result != TNeverType() and
1480+
result != TUnknownType()
14821481
}
14831482

14841483
pragma[nomagic]
14851484
private Type getComplexStrippedType(string derefChain, boolean borrow, TypePath strippedTypePath) {
1486-
result =
1487-
this.getACandidateReceiverTypeAtSubstituteLookupTraits(derefChain, borrow, strippedTypePath) and
1485+
result = this.getANonPseudoCandidateReceiverTypeAt(derefChain, borrow, strippedTypePath) and
14881486
isComplexRootStripped(strippedTypePath, result)
14891487
}
14901488

@@ -1523,23 +1521,58 @@ private module MethodResolution {
15231521
)
15241522
}
15251523

1524+
// forex using recursion
1525+
pragma[nomagic]
1526+
private predicate hasNoCompatibleTargetNoBorrowToIndex(
1527+
string derefChain, TypePath strippedTypePath, Type strippedType, int n
1528+
) {
1529+
(
1530+
this.supportsAutoDerefAndBorrow()
1531+
or
1532+
// needed for the `hasNoCompatibleTarget` check in
1533+
// `ReceiverSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate`
1534+
derefChain = ""
1535+
) and
1536+
strippedType = this.getComplexStrippedType(derefChain, false, strippedTypePath) and
1537+
n = -1
1538+
or
1539+
this.hasNoCompatibleTargetNoBorrowToIndex(derefChain, strippedTypePath, strippedType, n - 1) and
1540+
exists(Type t | t = getNthLookupType(strippedType, n) |
1541+
this.hasNoCompatibleTargetCheck(derefChain, false, strippedTypePath, t)
1542+
)
1543+
}
1544+
15261545
/**
15271546
* Holds if the candidate receiver type represented by `derefChain` does not
15281547
* have a matching method target.
15291548
*/
15301549
pragma[nomagic]
15311550
predicate hasNoCompatibleTargetNoBorrow(string derefChain) {
1551+
exists(Type strippedType |
1552+
this.hasNoCompatibleTargetNoBorrowToIndex(derefChain, _, strippedType,
1553+
getLastLookupTypeIndex(strippedType))
1554+
)
1555+
}
1556+
1557+
// forex using recursion
1558+
pragma[nomagic]
1559+
private predicate hasNoCompatibleNonBlanketTargetNoBorrowToIndex(
1560+
string derefChain, TypePath strippedTypePath, Type strippedType, int n
1561+
) {
15321562
(
15331563
this.supportsAutoDerefAndBorrow()
15341564
or
15351565
// needed for the `hasNoCompatibleTarget` check in
15361566
// `ReceiverSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate`
15371567
derefChain = ""
15381568
) and
1539-
exists(TypePath strippedTypePath, Type strippedType |
1540-
not derefChain.matches("%.ref") and // no need to try a borrow if the last thing we did was a deref
1541-
strippedType = this.getComplexStrippedType(derefChain, false, strippedTypePath) and
1542-
this.hasNoCompatibleTargetCheck(derefChain, false, strippedTypePath, strippedType)
1569+
strippedType = this.getComplexStrippedType(derefChain, false, strippedTypePath) and
1570+
n = -1
1571+
or
1572+
this.hasNoCompatibleNonBlanketTargetNoBorrowToIndex(derefChain, strippedTypePath,
1573+
strippedType, n - 1) and
1574+
exists(Type t | t = getNthLookupType(strippedType, n) |
1575+
this.hasNoCompatibleNonBlanketTargetCheck(derefChain, false, strippedTypePath, t)
15431576
)
15441577
}
15451578

@@ -1549,17 +1582,24 @@ private module MethodResolution {
15491582
*/
15501583
pragma[nomagic]
15511584
predicate hasNoCompatibleNonBlanketTargetNoBorrow(string derefChain) {
1552-
(
1553-
this.supportsAutoDerefAndBorrow()
1554-
or
1555-
// needed for the `hasNoCompatibleTarget` check in
1556-
// `ReceiverSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate`
1557-
derefChain = ""
1558-
) and
1559-
exists(TypePath strippedTypePath, Type strippedType |
1560-
not derefChain.matches("%.ref") and // no need to try a borrow if the last thing we did was a deref
1561-
strippedType = this.getComplexStrippedType(derefChain, false, strippedTypePath) and
1562-
this.hasNoCompatibleNonBlanketTargetCheck(derefChain, false, strippedTypePath, strippedType)
1585+
exists(Type strippedType |
1586+
this.hasNoCompatibleNonBlanketTargetNoBorrowToIndex(derefChain, _, strippedType,
1587+
getLastLookupTypeIndex(strippedType))
1588+
)
1589+
}
1590+
1591+
// forex using recursion
1592+
pragma[nomagic]
1593+
private predicate hasNoCompatibleTargetBorrowToIndex(
1594+
string derefChain, TypePath strippedTypePath, Type strippedType, int n
1595+
) {
1596+
this.hasNoCompatibleTargetNoBorrow(derefChain) and
1597+
strippedType = this.getComplexStrippedType(derefChain, true, strippedTypePath) and
1598+
n = -1
1599+
or
1600+
this.hasNoCompatibleTargetBorrowToIndex(derefChain, strippedTypePath, strippedType, n - 1) and
1601+
exists(Type t | t = getNthLookupType(strippedType, n) |
1602+
this.hasNoCompatibleNonBlanketLikeTargetCheck(derefChain, true, strippedTypePath, t)
15631603
)
15641604
}
15651605

@@ -1569,11 +1609,25 @@ private module MethodResolution {
15691609
*/
15701610
pragma[nomagic]
15711611
predicate hasNoCompatibleTargetBorrow(string derefChain) {
1572-
exists(TypePath strippedTypePath, Type strippedType |
1573-
this.hasNoCompatibleTargetNoBorrow(derefChain) and
1574-
strippedType = this.getComplexStrippedType(derefChain, true, strippedTypePath) and
1575-
this.hasNoCompatibleNonBlanketLikeTargetCheck(derefChain, true, strippedTypePath,
1576-
strippedType)
1612+
exists(Type strippedType |
1613+
this.hasNoCompatibleTargetBorrowToIndex(derefChain, _, strippedType,
1614+
getLastLookupTypeIndex(strippedType))
1615+
)
1616+
}
1617+
1618+
// forex using recursion
1619+
pragma[nomagic]
1620+
private predicate hasNoCompatibleNonBlanketTargetBorrowToIndex(
1621+
string derefChain, TypePath strippedTypePath, Type strippedType, int n
1622+
) {
1623+
this.hasNoCompatibleTargetNoBorrow(derefChain) and
1624+
strippedType = this.getComplexStrippedType(derefChain, true, strippedTypePath) and
1625+
n = -1
1626+
or
1627+
this.hasNoCompatibleNonBlanketTargetBorrowToIndex(derefChain, strippedTypePath, strippedType,
1628+
n - 1) and
1629+
exists(Type t | t = getNthLookupType(strippedType, n) |
1630+
this.hasNoCompatibleNonBlanketTargetCheck(derefChain, true, strippedTypePath, t)
15771631
)
15781632
}
15791633

@@ -1583,10 +1637,9 @@ private module MethodResolution {
15831637
*/
15841638
pragma[nomagic]
15851639
predicate hasNoCompatibleNonBlanketTargetBorrow(string derefChain) {
1586-
exists(TypePath strippedTypePath, Type strippedType |
1587-
this.hasNoCompatibleTargetNoBorrow(derefChain) and
1588-
strippedType = this.getComplexStrippedType(derefChain, true, strippedTypePath) and
1589-
this.hasNoCompatibleNonBlanketTargetCheck(derefChain, true, strippedTypePath, strippedType)
1640+
exists(Type strippedType |
1641+
this.hasNoCompatibleNonBlanketTargetBorrowToIndex(derefChain, _, strippedType,
1642+
getLastLookupTypeIndex(strippedType))
15901643
)
15911644
}
15921645

@@ -1804,9 +1857,8 @@ private module MethodResolution {
18041857
MethodCall getMethodCall() { result = mc_ }
18051858

18061859
Type getTypeAt(TypePath path) {
1807-
result = mc_.getACandidateReceiverTypeAtSubstituteLookupTraits(derefChain, borrow, path) and
1808-
not result = TNeverType() and
1809-
not result = TUnknownType()
1860+
result =
1861+
substituteLookupTraits(mc_.getANonPseudoCandidateReceiverTypeAt(derefChain, borrow, path))
18101862
}
18111863

18121864
pragma[nomagic]

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

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ class AssocFunctionType extends MkAssocFunctionType {
194194
Location getLocation() { result = this.getTypeMention().getLocation() }
195195
}
196196

197+
pragma[nomagic]
197198
private Trait getALookupTrait(Type t) {
198199
result = t.(TypeParamTypeParameter).getTypeParam().(TypeParamItemNode).resolveABound()
199200
or
@@ -208,14 +209,38 @@ private Trait getALookupTrait(Type t) {
208209
* Gets the type obtained by substituting in relevant traits in which to do function
209210
* lookup, or `t` itself when no such trait exist.
210211
*/
211-
bindingset[t]
212+
pragma[nomagic]
212213
Type substituteLookupTraits(Type t) {
213214
not exists(getALookupTrait(t)) and
214215
result = t
215216
or
216217
result = TTrait(getALookupTrait(t))
217218
}
218219

220+
/**
221+
* Gets the `n`th `substituteLookupTraits` type for `t`, per some abitrary order.
222+
*/
223+
pragma[nomagic]
224+
Type getNthLookupType(Type t, int n) {
225+
not exists(getALookupTrait(t)) and
226+
result = t and
227+
n = 0
228+
or
229+
result =
230+
TTrait(rank[n + 1](Trait trait, int i |
231+
trait = getALookupTrait(t) and
232+
i = idOfTypeParameterAstNode(trait)
233+
|
234+
trait order by i
235+
))
236+
}
237+
238+
/**
239+
* Gets the index of the last `substituteLookupTraits` type for `t`.
240+
*/
241+
pragma[nomagic]
242+
int getLastLookupTypeIndex(Type t) { result = max(int n | exists(getNthLookupType(t, n))) }
243+
219244
/**
220245
* A wrapper around `IsInstantiationOf` which ensures to substitute in lookup
221246
* traits when checking whether argument types are instantiations of function

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ multipleResolvedTargets
1313
| dyn_type.rs:90:10:90:13 | * ... |
1414
| invalid/main.rs:69:13:69:17 | * ... |
1515
| invalid/main.rs:76:13:76:17 | * ... |
16-
| main.rs:841:9:841:14 | x.m2() |
17-
| main.rs:842:9:842:14 | y.m2() |
1816
| main.rs:1092:14:1092:18 | * ... |
1917
| main.rs:1174:26:1174:30 | * ... |
2018
| main.rs:1519:14:1519:21 | * ... |

rust/ql/test/library-tests/type-inference/blanket_impl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ mod blanket_like_impl {
236236
impl MyTrait2 for &&S1 {
237237
// MyTrait2RefRefS1::m2
238238
fn m2(self) {
239-
self.m1() // $ MISSING: target=S1::m1
239+
self.m1() // $ target=S1::m1
240240
}
241241
}
242242

rust/ql/test/library-tests/type-inference/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -838,8 +838,8 @@ mod function_trait_bounds {
838838
}
839839

840840
fn bound_overlap<T: MyTrait2 + MyTrait3>(x: T, y: &T) {
841-
x.m2(); // $ target=MyTrait2::m2 $ SPURIOUS: target=MyTrait3::m2
842-
y.m2(); // $ target=MyTrait3::m2 $ SPURIOUS: target=MyTrait2::m2
841+
x.m2(); // $ target=MyTrait2::m2
842+
y.m2(); // $ target=MyTrait3::m2
843843
}
844844

845845
pub fn f() {

0 commit comments

Comments
 (0)