Merge pull request #19657 from hvitved/rust/type-inference-index-expr-simple

Rust: Simple type inference for index expressions
This commit is contained in:
Tom Hvitved
2025-06-12 10:27:09 +02:00
committed by GitHub
6 changed files with 248 additions and 9 deletions

View File

@@ -16,10 +16,13 @@ newtype TType =
TArrayType() or // todo: add size?
TRefType() or // todo: add mut?
TImplTraitType(ImplTraitTypeRepr impl) or
TSliceType() or
TTypeParamTypeParameter(TypeParam t) or
TAssociatedTypeTypeParameter(TypeAlias t) { any(TraitItemNode trait).getAnAssocItem() = t } or
TArrayTypeParameter() or
TRefTypeParameter() or
TSelfTypeParameter(Trait t)
TSelfTypeParameter(Trait t) or
TSliceTypeParameter()
/**
* A type without type arguments.
@@ -149,7 +152,8 @@ class ArrayType extends Type, TArrayType {
override TupleField getTupleField(int i) { none() }
override TypeParameter getTypeParameter(int i) {
none() // todo
result = TArrayTypeParameter() and
i = 0
}
override string toString() { result = "[]" }
@@ -227,6 +231,29 @@ class ImplTraitReturnType extends ImplTraitType {
override Function getFunction() { result = function }
}
/**
* A slice type.
*
* Slice types like `[i64]` are modeled as normal generic types
* with a single type argument.
*/
class SliceType extends Type, TSliceType {
SliceType() { this = TSliceType() }
override StructField getStructField(string name) { none() }
override TupleField getTupleField(int i) { none() }
override TypeParameter getTypeParameter(int i) {
result = TSliceTypeParameter() and
i = 0
}
override string toString() { result = "[]" }
override Location getLocation() { result instanceof EmptyLocation }
}
/** A type parameter. */
abstract class TypeParameter extends Type {
override StructField getStructField(string name) { none() }
@@ -306,6 +333,13 @@ class AssociatedTypeTypeParameter extends TypeParameter, TAssociatedTypeTypePara
override Location getLocation() { result = typeAlias.getLocation() }
}
/** An implicit array type parameter. */
class ArrayTypeParameter extends TypeParameter, TArrayTypeParameter {
override string toString() { result = "[T;...]" }
override Location getLocation() { result instanceof EmptyLocation }
}
/** An implicit reference type parameter. */
class RefTypeParameter extends TypeParameter, TRefTypeParameter {
override string toString() { result = "&T" }
@@ -313,6 +347,13 @@ class RefTypeParameter extends TypeParameter, TRefTypeParameter {
override Location getLocation() { result instanceof EmptyLocation }
}
/** An implicit slice type parameter. */
class SliceTypeParameter extends TypeParameter, TSliceTypeParameter {
override string toString() { result = "[T]" }
override Location getLocation() { result instanceof EmptyLocation }
}
/**
* The implicit `Self` type parameter of a trait, that refers to the
* implementing type of the trait.

View File

@@ -80,10 +80,18 @@ private module Input1 implements InputSig1<Location> {
int getTypeParameterId(TypeParameter tp) {
tp =
rank[result](TypeParameter tp0, int kind, int id |
tp0 instanceof RefTypeParameter and
tp0 instanceof ArrayTypeParameter and
kind = 0 and
id = 0
or
tp0 instanceof RefTypeParameter and
kind = 0 and
id = 1
or
tp0 instanceof SliceTypeParameter and
kind = 0 and
id = 2
or
kind = 1 and
exists(AstNode node | id = idOfTypeParameterAstNode(node) |
node = tp0.(TypeParamTypeParameter).getTypeParam() or
@@ -1128,6 +1136,50 @@ private Type inferAwaitExprType(AstNode n, TypePath path) {
)
}
private class Vec extends Struct {
Vec() { this.getCanonicalPath() = "alloc::vec::Vec" }
TypeParamTypeParameter getElementTypeParameter() {
result.getTypeParam() = this.getGenericParamList().getTypeParam(0)
}
}
/**
* According to [the Rust reference][1]: _"array and slice-typed expressions
* can be indexed with a `usize` index ... For other types an index expression
* `a[b]` is equivalent to *std::ops::Index::index(&a, b)"_.
*
* The logic below handles array and slice indexing, but for other types it is
* currently limited to `Vec`.
*
* [1]: https://doc.rust-lang.org/reference/expressions/array-expr.html#r-expr.array.index
*/
pragma[nomagic]
private Type inferIndexExprType(IndexExpr ie, TypePath path) {
// TODO: Should be implemented as method resolution, using the special
// `std::ops::Index` trait.
exists(TypePath exprPath, Builtins::BuiltinType t |
TStruct(t) = inferType(ie.getIndex()) and
(
// also allow `i32`, since that is currently the type that we infer for
// integer literals like `0`
t instanceof Builtins::I32
or
t instanceof Builtins::Usize
) and
result = inferType(ie.getBase(), exprPath)
|
exprPath.isCons(any(Vec v).getElementTypeParameter(), path)
or
exprPath.isCons(any(ArrayTypeParameter tp), path)
or
exists(TypePath path0 |
exprPath.isCons(any(RefTypeParameter tp), path0) and
path0.isCons(any(SliceTypeParameter tp), path)
)
)
}
private module MethodCall {
/** An expression that calls a method. */
abstract private class MethodCallImpl extends Expr {
@@ -1487,6 +1539,8 @@ private module Cached {
path.isEmpty()
or
result = inferAwaitExprType(n, path)
or
result = inferIndexExprType(n, path)
}
}

View File

@@ -43,6 +43,12 @@ class RefTypeReprMention extends TypeMention instanceof RefTypeRepr {
override Type resolveType() { result = TRefType() }
}
class SliceTypeReprMention extends TypeMention instanceof SliceTypeRepr {
override TypeMention getTypeArgument(int i) { result = super.getTypeRepr() and i = 0 }
override Type resolveType() { result = TSliceType() }
}
class PathTypeReprMention extends TypeMention instanceof PathTypeRepr {
Path path;
ItemNode resolved;

View File

@@ -1753,6 +1753,58 @@ mod impl_trait {
}
}
mod indexers {
use std::ops::Index;
#[derive(Debug)]
struct S;
impl S {
fn foo(&self) -> Self {
S
}
}
#[derive(Debug)]
struct MyVec<T> {
data: Vec<T>,
}
impl<T> MyVec<T> {
fn new() -> Self {
MyVec { data: Vec::new() }
}
fn push(&mut self, value: T) {
self.data.push(value); // $ fieldof=MyVec method=push
}
}
impl<T> Index<usize> for MyVec<T> {
type Output = T;
// MyVec::index
fn index(&self, index: usize) -> &Self::Output {
&self.data[index] // $ fieldof=MyVec
}
}
fn analyze_slice(slice: &[S]) {
let x = slice[0].foo(); // $ method=foo type=x:S
}
pub fn f() {
let mut vec = MyVec::new(); // $ type=vec:T.S
vec.push(S); // $ method=push
vec[0].foo(); // $ MISSING: method=foo -- type inference does not support the `Index` trait yet
let xs: [S; 1] = [S];
let x = xs[0].foo(); // $ method=foo type=x:S
analyze_slice(&xs);
}
}
fn main() {
field_access::f();
method_impl::f();
@@ -1774,4 +1826,5 @@ fn main() {
operators::f();
async_::f();
impl_trait::f();
indexers::f();
}

View File

@@ -2525,9 +2525,91 @@ inferType
| main.rs:1752:13:1752:13 | d | | main.rs:1700:5:1700:14 | S2 |
| main.rs:1752:17:1752:34 | uses_my_trait2(...) | | main.rs:1700:5:1700:14 | S2 |
| main.rs:1752:32:1752:33 | S1 | | main.rs:1699:5:1699:14 | S1 |
| main.rs:1758:5:1758:20 | ...::f(...) | | main.rs:67:5:67:21 | Foo |
| main.rs:1759:5:1759:60 | ...::g(...) | | main.rs:67:5:67:21 | Foo |
| main.rs:1759:20:1759:38 | ...::Foo {...} | | main.rs:67:5:67:21 | Foo |
| main.rs:1759:41:1759:59 | ...::Foo {...} | | main.rs:67:5:67:21 | Foo |
| main.rs:1775:5:1775:15 | ...::f(...) | | {EXTERNAL LOCATION} | trait Future |
| main.rs:1763:16:1763:20 | SelfParam | | file://:0:0:0:0 | & |
| main.rs:1763:16:1763:20 | SelfParam | &T | main.rs:1759:5:1760:13 | S |
| main.rs:1763:31:1765:9 | { ... } | | main.rs:1759:5:1760:13 | S |
| main.rs:1764:13:1764:13 | S | | main.rs:1759:5:1760:13 | S |
| main.rs:1774:26:1776:9 | { ... } | | main.rs:1768:5:1771:5 | MyVec |
| main.rs:1774:26:1776:9 | { ... } | T | main.rs:1773:10:1773:10 | T |
| main.rs:1775:13:1775:38 | MyVec {...} | | main.rs:1768:5:1771:5 | MyVec |
| main.rs:1775:13:1775:38 | MyVec {...} | T | main.rs:1773:10:1773:10 | T |
| main.rs:1775:27:1775:36 | ...::new(...) | | {EXTERNAL LOCATION} | Vec |
| main.rs:1775:27:1775:36 | ...::new(...) | T | main.rs:1773:10:1773:10 | T |
| main.rs:1778:17:1778:25 | SelfParam | | file://:0:0:0:0 | & |
| main.rs:1778:17:1778:25 | SelfParam | &T | main.rs:1768:5:1771:5 | MyVec |
| main.rs:1778:17:1778:25 | SelfParam | &T.T | main.rs:1773:10:1773:10 | T |
| main.rs:1778:28:1778:32 | value | | main.rs:1773:10:1773:10 | T |
| main.rs:1779:13:1779:16 | self | | file://:0:0:0:0 | & |
| main.rs:1779:13:1779:16 | self | &T | main.rs:1768:5:1771:5 | MyVec |
| main.rs:1779:13:1779:16 | self | &T.T | main.rs:1773:10:1773:10 | T |
| main.rs:1779:13:1779:21 | self.data | | {EXTERNAL LOCATION} | Vec |
| main.rs:1779:13:1779:21 | self.data | T | main.rs:1773:10:1773:10 | T |
| main.rs:1779:28:1779:32 | value | | main.rs:1773:10:1773:10 | T |
| main.rs:1787:18:1787:22 | SelfParam | | file://:0:0:0:0 | & |
| main.rs:1787:18:1787:22 | SelfParam | &T | main.rs:1768:5:1771:5 | MyVec |
| main.rs:1787:18:1787:22 | SelfParam | &T.T | main.rs:1783:10:1783:10 | T |
| main.rs:1787:25:1787:29 | index | | {EXTERNAL LOCATION} | usize |
| main.rs:1787:56:1789:9 | { ... } | | file://:0:0:0:0 | & |
| main.rs:1787:56:1789:9 | { ... } | &T | main.rs:1783:10:1783:10 | T |
| main.rs:1788:13:1788:29 | &... | | file://:0:0:0:0 | & |
| main.rs:1788:13:1788:29 | &... | &T | main.rs:1783:10:1783:10 | T |
| main.rs:1788:14:1788:17 | self | | file://:0:0:0:0 | & |
| main.rs:1788:14:1788:17 | self | &T | main.rs:1768:5:1771:5 | MyVec |
| main.rs:1788:14:1788:17 | self | &T.T | main.rs:1783:10:1783:10 | T |
| main.rs:1788:14:1788:22 | self.data | | {EXTERNAL LOCATION} | Vec |
| main.rs:1788:14:1788:22 | self.data | T | main.rs:1783:10:1783:10 | T |
| main.rs:1788:14:1788:29 | ...[index] | | main.rs:1783:10:1783:10 | T |
| main.rs:1788:24:1788:28 | index | | {EXTERNAL LOCATION} | usize |
| main.rs:1792:22:1792:26 | slice | | file://:0:0:0:0 | & |
| main.rs:1792:22:1792:26 | slice | &T | file://:0:0:0:0 | [] |
| main.rs:1792:22:1792:26 | slice | &T.[T] | main.rs:1759:5:1760:13 | S |
| main.rs:1793:13:1793:13 | x | | main.rs:1759:5:1760:13 | S |
| main.rs:1793:17:1793:21 | slice | | file://:0:0:0:0 | & |
| main.rs:1793:17:1793:21 | slice | &T | file://:0:0:0:0 | [] |
| main.rs:1793:17:1793:21 | slice | &T.[T] | main.rs:1759:5:1760:13 | S |
| main.rs:1793:17:1793:24 | slice[0] | | main.rs:1759:5:1760:13 | S |
| main.rs:1793:17:1793:30 | ... .foo() | | main.rs:1759:5:1760:13 | S |
| main.rs:1793:23:1793:23 | 0 | | {EXTERNAL LOCATION} | i32 |
| main.rs:1797:13:1797:19 | mut vec | | main.rs:1768:5:1771:5 | MyVec |
| main.rs:1797:13:1797:19 | mut vec | T | main.rs:1759:5:1760:13 | S |
| main.rs:1797:23:1797:34 | ...::new(...) | | main.rs:1768:5:1771:5 | MyVec |
| main.rs:1797:23:1797:34 | ...::new(...) | T | main.rs:1759:5:1760:13 | S |
| main.rs:1798:9:1798:11 | vec | | main.rs:1768:5:1771:5 | MyVec |
| main.rs:1798:9:1798:11 | vec | T | main.rs:1759:5:1760:13 | S |
| main.rs:1798:18:1798:18 | S | | main.rs:1759:5:1760:13 | S |
| main.rs:1799:9:1799:11 | vec | | main.rs:1768:5:1771:5 | MyVec |
| main.rs:1799:9:1799:11 | vec | T | main.rs:1759:5:1760:13 | S |
| main.rs:1799:13:1799:13 | 0 | | {EXTERNAL LOCATION} | i32 |
| main.rs:1801:13:1801:14 | xs | | file://:0:0:0:0 | [] |
| main.rs:1801:13:1801:14 | xs | | file://:0:0:0:0 | [] |
| main.rs:1801:13:1801:14 | xs | [T;...] | main.rs:1759:5:1760:13 | S |
| main.rs:1801:13:1801:14 | xs | [T] | main.rs:1759:5:1760:13 | S |
| main.rs:1801:21:1801:21 | 1 | | {EXTERNAL LOCATION} | i32 |
| main.rs:1801:26:1801:28 | [...] | | file://:0:0:0:0 | [] |
| main.rs:1801:26:1801:28 | [...] | | file://:0:0:0:0 | [] |
| main.rs:1801:26:1801:28 | [...] | [T;...] | main.rs:1759:5:1760:13 | S |
| main.rs:1801:26:1801:28 | [...] | [T] | main.rs:1759:5:1760:13 | S |
| main.rs:1801:27:1801:27 | S | | main.rs:1759:5:1760:13 | S |
| main.rs:1802:13:1802:13 | x | | main.rs:1759:5:1760:13 | S |
| main.rs:1802:17:1802:18 | xs | | file://:0:0:0:0 | [] |
| main.rs:1802:17:1802:18 | xs | | file://:0:0:0:0 | [] |
| main.rs:1802:17:1802:18 | xs | [T;...] | main.rs:1759:5:1760:13 | S |
| main.rs:1802:17:1802:18 | xs | [T] | main.rs:1759:5:1760:13 | S |
| main.rs:1802:17:1802:21 | xs[0] | | main.rs:1759:5:1760:13 | S |
| main.rs:1802:17:1802:27 | ... .foo() | | main.rs:1759:5:1760:13 | S |
| main.rs:1802:20:1802:20 | 0 | | {EXTERNAL LOCATION} | i32 |
| main.rs:1804:23:1804:25 | &xs | | file://:0:0:0:0 | & |
| main.rs:1804:23:1804:25 | &xs | &T | file://:0:0:0:0 | [] |
| main.rs:1804:23:1804:25 | &xs | &T | file://:0:0:0:0 | [] |
| main.rs:1804:23:1804:25 | &xs | &T.[T;...] | main.rs:1759:5:1760:13 | S |
| main.rs:1804:23:1804:25 | &xs | &T.[T] | main.rs:1759:5:1760:13 | S |
| main.rs:1804:24:1804:25 | xs | | file://:0:0:0:0 | [] |
| main.rs:1804:24:1804:25 | xs | | file://:0:0:0:0 | [] |
| main.rs:1804:24:1804:25 | xs | [T;...] | main.rs:1759:5:1760:13 | S |
| main.rs:1804:24:1804:25 | xs | [T] | main.rs:1759:5:1760:13 | S |
| main.rs:1810:5:1810:20 | ...::f(...) | | main.rs:67:5:67:21 | Foo |
| main.rs:1811:5:1811:60 | ...::g(...) | | main.rs:67:5:67:21 | Foo |
| main.rs:1811:20:1811:38 | ...::Foo {...} | | main.rs:67:5:67:21 | Foo |
| main.rs:1811:41:1811:59 | ...::Foo {...} | | main.rs:67:5:67:21 | Foo |
| main.rs:1827:5:1827:15 | ...::f(...) | | {EXTERNAL LOCATION} | trait Future |
testFailures

View File

@@ -55,10 +55,13 @@ module TypeTest implements TestSig {
exists(AstNode n, TypePath path, Type t |
t = TypeInference::inferType(n, path) and
location = n.getLocation() and
element = n.toString() and
if path.isEmpty()
then value = element + ":" + t
else value = element + ":" + path.toString() + "." + t.toString()
|
element = n.toString()
or
element = n.(IdentPat).getName().getText()
)
}
}