Rust: Simple type inference for index expressions

This commit is contained in:
Tom Hvitved
2025-06-03 19:02:13 +02:00
parent 301bd44a4c
commit 133aca0773
5 changed files with 132 additions and 6 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

@@ -1790,16 +1790,16 @@ mod indexers {
}
fn analyze_slice(slice: &[S]) {
let x = slice[0].foo(); // $ MISSING: method=foo MISSING: type=x: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
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(); // $ MISSING: method=foo MISSING: type=x:S
let x = xs[0].foo(); // $ method=foo type=x:S
analyze_slice(&xs);
}

View File

@@ -2561,7 +2561,14 @@ inferType
| 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 |
@@ -2574,14 +2581,32 @@ inferType
| 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 |