Skip to content

Commit 75caa18

Browse files
authored
Merge pull request #19724 from github/rust/type-inference-borrow
Rust: Implement type inference for ref expression as type equality
2 parents fbcd9ea + 01701de commit 75caa18

File tree

3 files changed

+1126
-1073
lines changed

3 files changed

+1126
-1073
lines changed

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

Lines changed: 8 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,10 @@ private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePat
257257
)
258258
)
259259
or
260+
n1 = n2.(RefExpr).getExpr() and
261+
prefix1.isEmpty() and
262+
prefix2 = TypePath::singleton(TRefTypeParameter())
263+
or
260264
n1 = n2.(DerefExpr).getExpr() and
261265
prefix1 = TypePath::singleton(TRefTypeParameter()) and
262266
prefix2.isEmpty()
@@ -973,43 +977,9 @@ private Type inferFieldExprType(AstNode n, TypePath path) {
973977
)
974978
}
975979

976-
/**
977-
* Gets the type of `n` at `path`, where `n` is either a reference expression
978-
* `& x` or an expression `x` inside a reference expression `& x`.
979-
*/
980+
/** Gets the root type of the reference expression `re`. */
980981
pragma[nomagic]
981-
private Type inferRefExprType(Expr e, TypePath path) {
982-
exists(RefExpr re |
983-
e = re and
984-
path.isEmpty() and
985-
result = TRefType()
986-
or
987-
e = re and
988-
exists(TypePath exprPath | result = inferType(re.getExpr(), exprPath) |
989-
if exprPath.isCons(TRefTypeParameter(), _)
990-
then
991-
// `&x` simply means `x` when `x` already has reference type
992-
path = exprPath
993-
else (
994-
path = TypePath::cons(TRefTypeParameter(), exprPath) and
995-
not (exprPath.isEmpty() and result = TRefType())
996-
)
997-
)
998-
or
999-
e = re.getExpr() and
1000-
exists(TypePath exprPath, TypePath refPath, Type exprType |
1001-
result = inferType(re, exprPath) and
1002-
exprPath.isCons(TRefTypeParameter(), refPath) and
1003-
exprType = inferType(e)
1004-
|
1005-
if exprType = TRefType()
1006-
then
1007-
// `&x` simply means `x` when `x` already has reference type
1008-
path = exprPath
1009-
else path = refPath
1010-
)
1011-
)
1012-
}
982+
private Type inferRefExprType(RefExpr re) { exists(re) and result = TRefType() }
1013983

1014984
pragma[nomagic]
1015985
private Type inferTryExprType(TryExpr te, TypePath path) {
@@ -1505,7 +1475,8 @@ private module Cached {
15051475
or
15061476
result = inferFieldExprType(n, path)
15071477
or
1508-
result = inferRefExprType(n, path)
1478+
result = inferRefExprType(n) and
1479+
path.isEmpty()
15091480
or
15101481
result = inferTryExprType(n, path)
15111482
or

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,17 @@ mod implicit_self_borrow {
11541154
}
11551155

11561156
mod borrowed_typed {
1157+
#[derive(Debug, Copy, Clone, Default)]
1158+
struct MyFlag {
1159+
bool: bool,
1160+
}
1161+
1162+
impl MyFlag {
1163+
fn flip(&mut self) {
1164+
self.bool = !self.bool; // $ fieldof=MyFlag method=not
1165+
}
1166+
}
1167+
11571168
struct S;
11581169

11591170
impl S {
@@ -1179,6 +1190,14 @@ mod borrowed_typed {
11791190
x.f1(); // $ method=f1
11801191
x.f2(); // $ method=f2
11811192
S::f3(&x);
1193+
1194+
let n = **&&true; // $ type=n:bool
1195+
1196+
// In this example the type of `flag` must be inferred at the call to
1197+
// `flip` and flow through the borrow in the argument.
1198+
let mut flag = Default::default();
1199+
MyFlag::flip(&mut flag);
1200+
println!("{:?}", flag); // $ type=flag:MyFlag
11821201
}
11831202
}
11841203

0 commit comments

Comments
 (0)