about summary refs log tree commit diff
path: root/compiler/stable_mir/src
diff options
context:
space:
mode:
authorKirby Linvill <kjlinvill@gmail.com>2023-11-02 09:05:35 -0600
committerKirby Linvill <kjlinvill@gmail.com>2023-11-09 20:56:35 -0700
commitb1585983ccd83c8bf15e5e19ec594c00b8a37a2e (patch)
tree937af26e1a4ecc6e196f92a15bf00557c88d6a18 /compiler/stable_mir/src
parent0f44eb32f1123ac93ab404d74c295263ce468343 (diff)
downloadrust-b1585983ccd83c8bf15e5e19ec594c00b8a37a2e.tar.gz
rust-b1585983ccd83c8bf15e5e19ec594c00b8a37a2e.zip
Add stable MIR Projections support based on MIR structure
This commit includes richer projections for both Places and
UserTypeProjections. However, the tests only touch on Places. There are
also outstanding TODOs regarding how projections should be resolved to
produce Place types, and regarding if UserTypeProjections should just
contain ProjectionElem<(),()> objects as in MIR.
Diffstat (limited to 'compiler/stable_mir/src')
-rw-r--r--compiler/stable_mir/src/mir/body.rs117
1 files changed, 115 insertions, 2 deletions
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs
index 06933783685..69e83efdf43 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -398,22 +398,133 @@ pub enum Operand {
 pub struct Place {
     pub local: Local,
     /// projection out of a place (access a field, deref a pointer, etc)
-    pub projection: String,
+    pub projection: Vec<ProjectionElem<Local, Ty>>,
+}
+
+// TODO(klinvill): in MIR ProjectionElem is parameterized on the second Field argument and the Index
+// argument. This is so it can be used for both the rust provided Places (for which the projection
+// elements are of type ProjectionElem<Local, Ty>) and user-provided type annotations (for which the
+// projection elements are of type ProjectionElem<(), ()>). Should we do the same thing in Stable MIR?
+#[derive(Clone, Debug)]
+pub enum ProjectionElem<V, T> {
+    /// Dereference projections (e.g. `*_1`) project to the address referenced by the base place.
+    Deref,
+
+    /// A field projection (e.g., `f` in `_1.f`) project to a field in the base place. The field is
+    /// referenced by source-order index rather than the name of the field. The fields type is also
+    /// given.
+    Field(FieldIdx, T),
+
+    /// Index into a slice/array. The value of the index is computed at runtime using the `V`
+    /// argument.
+    ///
+    /// Note that this does not also dereference, and so it does not exactly correspond to slice
+    /// indexing in Rust. In other words, in the below Rust code:
+    ///
+    /// ```rust
+    /// let x = &[1, 2, 3, 4];
+    /// let i = 2;
+    /// x[i];
+    /// ```
+    ///
+    /// The `x[i]` is turned into a `Deref` followed by an `Index`, not just an `Index`. The same
+    /// thing is true of the `ConstantIndex` and `Subslice` projections below.
+    Index(V),
+
+    /// Index into a slice/array given by offsets.
+    ///
+    /// These indices are generated by slice patterns. Easiest to explain by example:
+    ///
+    /// ```ignore (illustrative)
+    /// [X, _, .._, _, _] => { offset: 0, min_length: 4, from_end: false },
+    /// [_, X, .._, _, _] => { offset: 1, min_length: 4, from_end: false },
+    /// [_, _, .._, X, _] => { offset: 2, min_length: 4, from_end: true },
+    /// [_, _, .._, _, X] => { offset: 1, min_length: 4, from_end: true },
+    /// ```
+    ConstantIndex {
+        /// index or -index (in Python terms), depending on from_end
+        offset: u64,
+        /// The thing being indexed must be at least this long. For arrays this
+        /// is always the exact length.
+        min_length: u64,
+        /// Counting backwards from end? This is always false when indexing an
+        /// array.
+        from_end: bool,
+    },
+
+    /// Projects a slice from the base place.
+    ///
+    /// These indices are generated by slice patterns. If `from_end` is true, this represents
+    /// `slice[from..slice.len() - to]`. Otherwise it represents `array[from..to]`.
+    Subslice {
+        from: u64,
+        to: u64,
+        /// Whether `to` counts from the start or end of the array/slice.
+        from_end: bool,
+    },
+
+    /// "Downcast" to a variant of an enum or a coroutine.
+    //
+    // TODO(klinvill): MIR includes an Option<Symbol> argument that is the name of the variant, used
+    // for printing MIR. However I don't see it used anywhere. Is such a field needed or can we just
+    // include the VariantIdx which could be used to recover the field name if needed?
+    Downcast(VariantIdx),
+
+    /// Like an explicit cast from an opaque type to a concrete type, but without
+    /// requiring an intermediate variable.
+    OpaqueCast(T),
+
+    /// A `Subtype(T)` projection is applied to any `StatementKind::Assign` where
+    /// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping
+    /// explicit during optimizations and codegen.
+    ///
+    /// This projection doesn't impact the runtime behavior of the program except for potentially changing
+    /// some type metadata of the interpreter or codegen backend.
+    Subtype(T),
 }
 
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct UserTypeProjection {
     pub base: UserTypeAnnotationIndex,
-    pub projection: String,
+
+    /// `UserTypeProjection` projections need neither the `V` parameter for `Index` nor the `T` for
+    /// `Field`.
+    pub projection: Vec<ProjectionElem<(), ()>>,
 }
 
 pub type Local = usize;
 
 pub const RETURN_LOCAL: Local = 0;
 
+/// The source-order index of a field in a variant.
+///
+/// For example, in the following types,
+/// ```rust
+/// enum Demo1 {
+///    Variant0 { a: bool, b: i32 },
+///    Variant1 { c: u8, d: u64 },
+/// }
+/// struct Demo2 { e: u8, f: u16, g: u8 }
+/// ```
+/// `a`'s `FieldIdx` is `0`,
+/// `b`'s `FieldIdx` is `1`,
+/// `c`'s `FieldIdx` is `0`, and
+/// `g`'s `FieldIdx` is `2`.
 type FieldIdx = usize;
 
 /// The source-order index of a variant in a type.
+///
+/// For example, in the following types,
+/// ```rust
+/// enum Demo1 {
+///    Variant0 { a: bool, b: i32 },
+///    Variant1 { c: u8, d: u64 },
+/// }
+/// struct Demo2 { e: u8, f: u16, g: u8 }
+/// ```
+/// `a` is in the variant with the `VariantIdx` of `0`,
+/// `c` is in the variant with the `VariantIdx` of `1`, and
+/// `g` is in the variant with the `VariantIdx` of `0`.
 pub type VariantIdx = usize;
 
 type UserTypeAnnotationIndex = usize;
@@ -536,6 +647,8 @@ impl Constant {
 }
 
 impl Place {
+    // TODO(klinvill): What is the expected behavior of this function? Should it resolve down the
+    // chain of projections so that `*(_1.f)` would end up returning the type referenced by `f`?
     pub fn ty(&self, locals: &[LocalDecl]) -> Ty {
         let _start_ty = locals[self.local].ty;
         todo!("Implement projection")