Skip to content

Commit e063ce4

Browse files
committed
Rust: Model Deref trait in type inference
1 parent 16d9655 commit e063ce4

File tree

6 files changed

+159
-39
lines changed

6 files changed

+159
-39
lines changed

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

Lines changed: 107 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,24 +1441,13 @@ private module MethodResolution {
14411441
* Same as `getACandidateReceiverTypeAt`, but without borrows.
14421442
*/
14431443
pragma[nomagic]
1444-
private Type getACandidateReceiverTypeAtNoBorrow(string derefChain, TypePath path) {
1444+
Type getACandidateReceiverTypeAtNoBorrow(string derefChain, TypePath path) {
14451445
result = this.getReceiverTypeAt(path) and
14461446
derefChain = ""
14471447
or
1448-
this.supportsAutoDerefAndBorrow() and
1449-
exists(TypePath path0, Type t0, string derefChain0 |
1450-
this.hasNoCompatibleTargetMutBorrow(derefChain0) and
1451-
t0 = this.getACandidateReceiverTypeAtNoBorrow(derefChain0, path0)
1452-
|
1453-
path0.isCons(getRefTypeParameter(), path) and
1454-
result = t0 and
1455-
derefChain = derefChain0 + ".ref"
1456-
or
1457-
path0.isEmpty() and
1458-
path = path0 and
1459-
t0 = getStringStruct() and
1460-
result = getStrStruct() and
1461-
derefChain = derefChain0 + ".str"
1448+
exists(ImplicitDeref::DerefImplItemNode impl, string derefChain0 |
1449+
result = ImplicitDeref::getDereferencedCandidateReceiverType(this, impl, derefChain0, path) and
1450+
derefChain = derefChain0 + "." + impl.getId()
14621451
)
14631452
}
14641453

@@ -1698,8 +1687,11 @@ private module MethodResolution {
16981687
}
16991688

17001689
predicate receiverHasImplicitDeref(AstNode receiver) {
1701-
exists(this.resolveCallTarget(_, ".ref", TNoBorrowKind())) and
1702-
receiver = this.getArg(any(ArgumentPosition pos | pos.isSelf()))
1690+
exists(ImplicitDeref::DerefImplItemNode impl |
1691+
impl.isRefImpl() and
1692+
exists(this.resolveCallTarget(_, "." + impl.getId(), TNoBorrowKind())) and
1693+
receiver = this.getArg(any(ArgumentPosition pos | pos.isSelf()))
1694+
)
17031695
}
17041696

17051697
predicate argumentHasImplicitBorrow(AstNode arg, BorrowKind borrow) {
@@ -1969,6 +1961,98 @@ private module MethodResolution {
19691961
Location getLocation() { result = mc_.getLocation() }
19701962
}
19711963

1964+
module ImplicitDeref {
1965+
private import codeql.rust.elements.internal.generated.Raw
1966+
private import codeql.rust.elements.internal.generated.Synth
1967+
1968+
/** An `impl` block that implements the `Deref` trait. */
1969+
class DerefImplItemNode extends ImplItemNode {
1970+
DerefImplItemNode() { this.resolveTraitTy() instanceof DerefTrait }
1971+
1972+
/**
1973+
* Holds if this `impl` block is the special `Deref` implementation for
1974+
* `&T` or `&mut T`:
1975+
*
1976+
* ```rust
1977+
* impl<T: ?Sized> const Deref for &T
1978+
* ```
1979+
*
1980+
* or
1981+
*
1982+
* ```rust
1983+
* impl<T: ?Sized> const Deref for &mut T
1984+
* ```
1985+
*/
1986+
predicate isRefImpl() { this.resolveSelfTyBuiltin() instanceof Builtins::RefType }
1987+
1988+
/** Gets an internal unique ID used to identify this block amongst all `Deref` impl blocks. */
1989+
int getId() { idOfRaw(Synth::convertAstNodeToRaw(this), result) }
1990+
}
1991+
1992+
private class DerefImplItemRaw extends Raw::Impl {
1993+
DerefImplItemRaw() { this = Synth::convertAstNodeToRaw(any(DerefImplItemNode i)) }
1994+
}
1995+
1996+
private predicate id(DerefImplItemRaw x, DerefImplItemRaw y) { x = y }
1997+
1998+
private predicate idOfRaw(DerefImplItemRaw x, int y) = equivalenceRelation(id/2)(x, y)
1999+
2000+
private newtype TMethodCallDerefCand =
2001+
MkMethodCallDerefCand(MethodCall mc, string derefChain) {
2002+
mc.supportsAutoDerefAndBorrow() and
2003+
mc.hasNoCompatibleTargetMutBorrow(derefChain) and
2004+
exists(mc.getACandidateReceiverTypeAtNoBorrow(derefChain, _))
2005+
}
2006+
2007+
private class MethodCallDerefCand extends MkMethodCallDerefCand {
2008+
MethodCall mc_;
2009+
string derefChain;
2010+
2011+
MethodCallDerefCand() { this = MkMethodCallDerefCand(mc_, derefChain) }
2012+
2013+
MethodCall getMethodCall() { result = mc_ }
2014+
2015+
Type getTypeAt(TypePath path) {
2016+
result = substituteLookupTraits(mc_.getACandidateReceiverTypeAtNoBorrow(derefChain, path)) and
2017+
not result = TNeverType() and
2018+
not result = TUnknownType()
2019+
}
2020+
2021+
string toString() { result = mc_.toString() + " [" + derefChain + "]" }
2022+
2023+
Location getLocation() { result = mc_.getLocation() }
2024+
}
2025+
2026+
private module MethodCallSatisfiesConstraintInput implements
2027+
SatisfiesConstraintInputSig<MethodCallDerefCand>
2028+
{
2029+
pragma[nomagic]
2030+
predicate relevantConstraint(MethodCallDerefCand mc, Type constraint) {
2031+
exists(mc) and
2032+
constraint.(TraitType).getTrait() instanceof DerefTrait
2033+
}
2034+
2035+
predicate useUniversalConditions() { none() }
2036+
}
2037+
2038+
pragma[nomagic]
2039+
private AssociatedTypeTypeParameter getDerefTargetTypeParameter() {
2040+
result.getTypeAlias() = any(DerefTrait ft).getTargetType()
2041+
}
2042+
2043+
pragma[nomagic]
2044+
Type getDereferencedCandidateReceiverType(
2045+
MethodCall mc, DerefImplItemNode impl, string derefChain, TypePath path
2046+
) {
2047+
exists(MethodCallDerefCand mcc, TypePath exprPath |
2048+
mcc = MkMethodCallDerefCand(mc, derefChain) and
2049+
SatisfiesConstraint<MethodCallDerefCand, MethodCallSatisfiesConstraintInput>::satisfiesConstraintType(mcc,
2050+
impl, _, exprPath, result) and
2051+
exprPath.isCons(getDerefTargetTypeParameter(), path)
2052+
)
2053+
}
2054+
}
2055+
19722056
private module ReceiverSatisfiesBlanketLikeConstraintInput implements
19732057
BlanketImplementation::SatisfiesBlanketConstraintInputSig<MethodCallCand>
19742058
{
@@ -2347,9 +2431,12 @@ private Type inferMethodCallType1(AstNode n, boolean isReturn, TypePath path) {
23472431
path = path0
23482432
or
23492433
// adjust for implicit deref
2350-
apos.isSelf() and
2351-
derefChainBorrow = ".ref;" and
2352-
path = TypePath::cons(getRefTypeParameter(), path0)
2434+
exists(MethodResolution::ImplicitDeref::DerefImplItemNode impl |
2435+
impl.isRefImpl() and
2436+
apos.isSelf() and
2437+
derefChainBorrow = "." + impl.getId() + ";" and
2438+
path = TypePath::cons(getRefTypeParameter(), path0)
2439+
)
23532440
or
23542441
// adjust for implicit borrow
23552442
apos.isSelf() and
@@ -3156,9 +3243,6 @@ private Type inferTryExprType(TryExpr te, TypePath path) {
31563243
pragma[nomagic]
31573244
private StructType getStrStruct() { result = TStruct(any(Builtins::Str s)) }
31583245

3159-
pragma[nomagic]
3160-
private StructType getStringStruct() { result = TStruct(any(StringStruct s)) }
3161-
31623246
pragma[nomagic]
31633247
private Type inferLiteralType(LiteralExpr le, TypePath path, boolean certain) {
31643248
path.isEmpty() and

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ mod basic_blanket_impl {
5353
println!("{x4:?}");
5454
let x5 = S1::duplicate(&S1); // $ target=Clone1duplicate
5555
println!("{x5:?}");
56-
let x6 = S2.duplicate(); // $ MISSING: target=Clone1duplicate
56+
let x6 = S2.duplicate(); // $ target=Clone1duplicate
5757
println!("{x6:?}");
58-
let x7 = (&S2).duplicate(); // $ MISSING: target=Clone1duplicate
58+
let x7 = (&S2).duplicate(); // $ target=Clone1duplicate
5959
println!("{x7:?}");
6060
}
6161
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,18 +102,18 @@ fn explicit_box_dereference() {
102102
fn implicit_dereference() {
103103
// Call method on implicitly dereferenced value
104104
let x = MyIntPointer { value: 34i64 };
105-
let _y = x.is_positive(); // $ MISSING: target=is_positive type=_y:bool
105+
let _y = x.is_positive(); // $ target=is_positive type=_y:bool
106106

107107
// Call method on implicitly dereferenced value
108108
let x = MySmartPointer { value: 34i64 };
109-
let _y = x.is_positive(); // $ MISSING: target=is_positive type=_y:bool
109+
let _y = x.is_positive(); // $ target=is_positive type=_y:bool
110110

111111
let z = MySmartPointer { value: S(0i64) };
112-
let z_ = z.foo(); // $ MISSING: target=foo type=z_:TRef.i64
112+
let z_ = z.foo(); // $ target=foo type=z_:TRef.i64
113113

114114
let v = Vec::new(); // $ target=new $ MISSING: type=x:T.i32
115115
let mut x = MySmartPointer { value: v };
116-
x.push(0); // $ MISSING: target=push
116+
x.push(0); // $ target=push
117117
}
118118

119119
mod implicit_deref_coercion_cycle {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2888,8 +2888,8 @@ pub mod path_buf {
28882888
let path3 = path2.unwrap(); // $ target=unwrap type=path3:PathBuf
28892889

28902890
let pathbuf1 = PathBuf::new(); // $ target=new certainType=pathbuf1:PathBuf
2891-
let pathbuf2 = pathbuf1.canonicalize(); // $ MISSING: target=canonicalize
2892-
let pathbuf3 = pathbuf2.unwrap(); // $ MISSING: target=unwrap type=pathbuf3:PathBuf
2891+
let pathbuf2 = pathbuf1.canonicalize(); // $ target=canonicalize
2892+
let pathbuf3 = pathbuf2.unwrap(); // $ target=unwrap type=pathbuf3:PathBuf
28932893
}
28942894
}
28952895

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4573,20 +4573,26 @@ inferType
45734573
| blanket_impl.rs:55:18:55:25 | ...::_print(...) | | {EXTERNAL LOCATION} | () |
45744574
| blanket_impl.rs:55:18:55:25 | { ... } | | {EXTERNAL LOCATION} | () |
45754575
| blanket_impl.rs:55:20:55:21 | x5 | | blanket_impl.rs:6:5:7:14 | S1 |
4576+
| blanket_impl.rs:56:13:56:14 | x6 | | blanket_impl.rs:6:5:7:14 | S1 |
45764577
| blanket_impl.rs:56:18:56:19 | S2 | | blanket_impl.rs:9:5:10:14 | S2 |
4578+
| blanket_impl.rs:56:18:56:31 | S2.duplicate() | | blanket_impl.rs:6:5:7:14 | S1 |
45774579
| blanket_impl.rs:57:18:57:25 | "{x6:?}\\n" | | {EXTERNAL LOCATION} | & |
45784580
| blanket_impl.rs:57:18:57:25 | "{x6:?}\\n" | TRef | {EXTERNAL LOCATION} | str |
45794581
| blanket_impl.rs:57:18:57:25 | ...::_print(...) | | {EXTERNAL LOCATION} | () |
45804582
| blanket_impl.rs:57:18:57:25 | { ... } | | {EXTERNAL LOCATION} | () |
4583+
| blanket_impl.rs:57:20:57:21 | x6 | | blanket_impl.rs:6:5:7:14 | S1 |
4584+
| blanket_impl.rs:58:13:58:14 | x7 | | blanket_impl.rs:6:5:7:14 | S1 |
45814585
| blanket_impl.rs:58:18:58:22 | (...) | | {EXTERNAL LOCATION} | & |
45824586
| blanket_impl.rs:58:18:58:22 | (...) | TRef | blanket_impl.rs:9:5:10:14 | S2 |
4587+
| blanket_impl.rs:58:18:58:34 | ... .duplicate() | | blanket_impl.rs:6:5:7:14 | S1 |
45834588
| blanket_impl.rs:58:19:58:21 | &S2 | | {EXTERNAL LOCATION} | & |
45844589
| blanket_impl.rs:58:19:58:21 | &S2 | TRef | blanket_impl.rs:9:5:10:14 | S2 |
45854590
| blanket_impl.rs:58:20:58:21 | S2 | | blanket_impl.rs:9:5:10:14 | S2 |
45864591
| blanket_impl.rs:59:18:59:25 | "{x7:?}\\n" | | {EXTERNAL LOCATION} | & |
45874592
| blanket_impl.rs:59:18:59:25 | "{x7:?}\\n" | TRef | {EXTERNAL LOCATION} | str |
45884593
| blanket_impl.rs:59:18:59:25 | ...::_print(...) | | {EXTERNAL LOCATION} | () |
45894594
| blanket_impl.rs:59:18:59:25 | { ... } | | {EXTERNAL LOCATION} | () |
4595+
| blanket_impl.rs:59:20:59:21 | x7 | | blanket_impl.rs:6:5:7:14 | S1 |
45904596
| blanket_impl.rs:68:24:68:24 | x | | {EXTERNAL LOCATION} | i64 |
45914597
| blanket_impl.rs:68:32:68:32 | y | | blanket_impl.rs:67:5:69:5 | Self [trait Trait1] |
45924598
| blanket_impl.rs:72:24:72:24 | x | | {EXTERNAL LOCATION} | i64 |
@@ -5214,14 +5220,18 @@ inferType
52145220
| dereference.rs:104:9:104:9 | x | | dereference.rs:5:1:7:1 | MyIntPointer |
52155221
| dereference.rs:104:13:104:41 | MyIntPointer {...} | | dereference.rs:5:1:7:1 | MyIntPointer |
52165222
| dereference.rs:104:35:104:39 | 34i64 | | {EXTERNAL LOCATION} | i64 |
5223+
| dereference.rs:105:9:105:10 | _y | | {EXTERNAL LOCATION} | bool |
52175224
| dereference.rs:105:14:105:14 | x | | dereference.rs:5:1:7:1 | MyIntPointer |
5225+
| dereference.rs:105:14:105:28 | x.is_positive() | | {EXTERNAL LOCATION} | bool |
52185226
| dereference.rs:108:9:108:9 | x | | dereference.rs:18:1:20:1 | MySmartPointer |
52195227
| dereference.rs:108:9:108:9 | x | T | {EXTERNAL LOCATION} | i64 |
52205228
| dereference.rs:108:13:108:43 | MySmartPointer {...} | | dereference.rs:18:1:20:1 | MySmartPointer |
52215229
| dereference.rs:108:13:108:43 | MySmartPointer {...} | T | {EXTERNAL LOCATION} | i64 |
52225230
| dereference.rs:108:37:108:41 | 34i64 | | {EXTERNAL LOCATION} | i64 |
5231+
| dereference.rs:109:9:109:10 | _y | | {EXTERNAL LOCATION} | bool |
52235232
| dereference.rs:109:14:109:14 | x | | dereference.rs:18:1:20:1 | MySmartPointer |
52245233
| dereference.rs:109:14:109:14 | x | T | {EXTERNAL LOCATION} | i64 |
5234+
| dereference.rs:109:14:109:28 | x.is_positive() | | {EXTERNAL LOCATION} | bool |
52255235
| dereference.rs:111:9:111:9 | z | | dereference.rs:18:1:20:1 | MySmartPointer |
52265236
| dereference.rs:111:9:111:9 | z | T | dereference.rs:38:1:38:15 | S |
52275237
| dereference.rs:111:9:111:9 | z | T.T | {EXTERNAL LOCATION} | i64 |
@@ -5231,9 +5241,13 @@ inferType
52315241
| dereference.rs:111:37:111:43 | S(...) | | dereference.rs:38:1:38:15 | S |
52325242
| dereference.rs:111:37:111:43 | S(...) | T | {EXTERNAL LOCATION} | i64 |
52335243
| dereference.rs:111:39:111:42 | 0i64 | | {EXTERNAL LOCATION} | i64 |
5244+
| dereference.rs:112:9:112:10 | z_ | | {EXTERNAL LOCATION} | & |
5245+
| dereference.rs:112:9:112:10 | z_ | TRef | {EXTERNAL LOCATION} | i64 |
52345246
| dereference.rs:112:14:112:14 | z | | dereference.rs:18:1:20:1 | MySmartPointer |
52355247
| dereference.rs:112:14:112:14 | z | T | dereference.rs:38:1:38:15 | S |
52365248
| dereference.rs:112:14:112:14 | z | T.T | {EXTERNAL LOCATION} | i64 |
5249+
| dereference.rs:112:14:112:20 | z.foo() | | {EXTERNAL LOCATION} | & |
5250+
| dereference.rs:112:14:112:20 | z.foo() | TRef | {EXTERNAL LOCATION} | i64 |
52375251
| dereference.rs:114:9:114:9 | v | | {EXTERNAL LOCATION} | Vec |
52385252
| dereference.rs:114:9:114:9 | v | A | {EXTERNAL LOCATION} | Global |
52395253
| dereference.rs:114:13:114:22 | ...::new(...) | | {EXTERNAL LOCATION} | Vec |
@@ -5249,6 +5263,7 @@ inferType
52495263
| dereference.rs:116:5:116:5 | x | | dereference.rs:18:1:20:1 | MySmartPointer |
52505264
| dereference.rs:116:5:116:5 | x | T | {EXTERNAL LOCATION} | Vec |
52515265
| dereference.rs:116:5:116:5 | x | T.A | {EXTERNAL LOCATION} | Global |
5266+
| dereference.rs:116:5:116:13 | x.push(...) | | {EXTERNAL LOCATION} | () |
52525267
| dereference.rs:116:12:116:12 | 0 | | {EXTERNAL LOCATION} | i32 |
52535268
| dereference.rs:143:19:151:5 | { ... } | | {EXTERNAL LOCATION} | () |
52545269
| dereference.rs:144:17:144:26 | key_to_key | | {EXTERNAL LOCATION} | HashMap |
@@ -10731,7 +10746,18 @@ inferType
1073110746
| main.rs:2888:21:2888:34 | path2.unwrap() | | main.rs:2865:5:2865:25 | PathBuf |
1073210747
| main.rs:2890:13:2890:20 | pathbuf1 | | main.rs:2865:5:2865:25 | PathBuf |
1073310748
| main.rs:2890:24:2890:37 | ...::new(...) | | main.rs:2865:5:2865:25 | PathBuf |
10749+
| main.rs:2891:13:2891:20 | pathbuf2 | | {EXTERNAL LOCATION} | Result |
10750+
| main.rs:2891:13:2891:20 | pathbuf2 | E | {EXTERNAL LOCATION} | () |
10751+
| main.rs:2891:13:2891:20 | pathbuf2 | T | main.rs:2865:5:2865:25 | PathBuf |
1073410752
| main.rs:2891:24:2891:31 | pathbuf1 | | main.rs:2865:5:2865:25 | PathBuf |
10753+
| main.rs:2891:24:2891:46 | pathbuf1.canonicalize() | | {EXTERNAL LOCATION} | Result |
10754+
| main.rs:2891:24:2891:46 | pathbuf1.canonicalize() | E | {EXTERNAL LOCATION} | () |
10755+
| main.rs:2891:24:2891:46 | pathbuf1.canonicalize() | T | main.rs:2865:5:2865:25 | PathBuf |
10756+
| main.rs:2892:13:2892:20 | pathbuf3 | | main.rs:2865:5:2865:25 | PathBuf |
10757+
| main.rs:2892:24:2892:31 | pathbuf2 | | {EXTERNAL LOCATION} | Result |
10758+
| main.rs:2892:24:2892:31 | pathbuf2 | E | {EXTERNAL LOCATION} | () |
10759+
| main.rs:2892:24:2892:31 | pathbuf2 | T | main.rs:2865:5:2865:25 | PathBuf |
10760+
| main.rs:2892:24:2892:40 | pathbuf2.unwrap() | | main.rs:2865:5:2865:25 | PathBuf |
1073510761
| main.rs:2898:14:2898:18 | SelfParam | | {EXTERNAL LOCATION} | & |
1073610762
| main.rs:2898:14:2898:18 | SelfParam | TRef | main.rs:2897:5:2899:5 | Self [trait MyTrait] |
1073710763
| main.rs:2905:14:2905:18 | SelfParam | | {EXTERNAL LOCATION} | & |

shared/typeinference/codeql/typeinference/internal/TypeInference.qll

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,9 +1059,10 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
10591059

10601060
pragma[nomagic]
10611061
private predicate satisfiesConstraintTypeMention1(
1062-
HasTypeTree tt, Type constraint, TypePath path, TypePath pathToTypeParamInSub
1062+
HasTypeTree tt, TypeAbstraction abs, Type constraint, TypePath path,
1063+
TypePath pathToTypeParamInSub
10631064
) {
1064-
exists(TypeAbstraction abs, TypeMention sub, TypeParameter tp |
1065+
exists(TypeMention sub, TypeParameter tp |
10651066
satisfiesConstraintTypeMention0(tt, constraint, abs, sub, path, tp) and
10661067
tp = abs.getATypeParameter() and
10671068
sub.resolveTypeAt(pathToTypeParamInSub) = tp
@@ -1073,17 +1074,26 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
10731074
* with the type `t` at `path`.
10741075
*/
10751076
pragma[nomagic]
1076-
predicate satisfiesConstraintType(HasTypeTree tt, Type constraint, TypePath path, Type t) {
1077-
exists(TypeAbstraction abs |
1078-
satisfiesConstraintTypeMention0(tt, constraint, abs, _, path, t) and
1079-
not t = abs.getATypeParameter()
1080-
)
1077+
predicate satisfiesConstraintType(
1078+
HasTypeTree tt, TypeAbstraction abs, Type constraint, TypePath path, Type t
1079+
) {
1080+
satisfiesConstraintTypeMention0(tt, constraint, abs, _, path, t) and
1081+
not t = abs.getATypeParameter()
10811082
or
10821083
exists(TypePath prefix0, TypePath pathToTypeParamInSub, TypePath suffix |
1083-
satisfiesConstraintTypeMention1(tt, constraint, prefix0, pathToTypeParamInSub) and
1084+
satisfiesConstraintTypeMention1(tt, abs, constraint, prefix0, pathToTypeParamInSub) and
10841085
tt.getTypeAt(pathToTypeParamInSub.appendInverse(suffix)) = t and
10851086
path = prefix0.append(suffix)
10861087
)
1088+
}
1089+
1090+
/**
1091+
* Holds if the type tree at `tt` satisfies the constraint `constraint`
1092+
* with the type `t` at `path`.
1093+
*/
1094+
pragma[nomagic]
1095+
predicate satisfiesConstraintType(HasTypeTree tt, Type constraint, TypePath path, Type t) {
1096+
satisfiesConstraintType(tt, _, constraint, path, t)
10871097
or
10881098
hasTypeConstraint(tt, constraint, constraint) and
10891099
t = tt.getTypeAt(path)

0 commit comments

Comments
 (0)