Skip to content

Commit 698fcc8

Browse files
committed
Auto merge of rust-lang#117517 - klinvill:smir-projections, r=ouz-a
Add richer structure for Stable MIR Projections Resolves rust-lang/project-stable-mir#49. Projections in Stable MIR are currently just strings. This PR replaces that representation with a richer structure, namely projections become vectors of `ProjectionElem`s, just as in MIR. The `ProjectionElem` enum is heavily based off of the MIR `ProjectionElem`. This PR is a draft since there are several outstanding issues to resolve, including: - How should `UserTypeProjection`s be represented in Stable MIR? In MIR, the projections are just a vector of `ProjectionElem<(),()>`, meaning `ProjectionElem`s that don't have Local or Type arguments (for `Index`, `Field`, etc. objects). Should `UserTypeProjection`s be represented this way in Stable MIR as well? Or is there a more user-friendly representation that wouldn't drag along all the `ProjectionElem` variants that presumably can't appear? - What is the expected behavior of a `Place`'s `ty` function? Should it resolve down the chain of projections so that something like `*_1.f` would return the type referenced by field `f`? - Tests should be added for `UserTypeProjection`
2 parents cc8681b + c036a10 commit 698fcc8

File tree

4 files changed

+355
-7
lines changed

4 files changed

+355
-7
lines changed

compiler/rustc_smir/src/rustc_smir/mod.rs

+38-4
Original file line numberDiff line numberDiff line change
@@ -682,19 +682,53 @@ impl<'tcx> Stable<'tcx> for mir::ConstOperand<'tcx> {
682682

683683
impl<'tcx> Stable<'tcx> for mir::Place<'tcx> {
684684
type T = stable_mir::mir::Place;
685-
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
685+
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
686686
stable_mir::mir::Place {
687687
local: self.local.as_usize(),
688-
projection: format!("{:?}", self.projection),
688+
projection: self.projection.iter().map(|e| e.stable(tables)).collect(),
689+
}
690+
}
691+
}
692+
693+
impl<'tcx> Stable<'tcx> for mir::PlaceElem<'tcx> {
694+
type T = stable_mir::mir::ProjectionElem;
695+
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
696+
use mir::ProjectionElem::*;
697+
match self {
698+
Deref => stable_mir::mir::ProjectionElem::Deref,
699+
Field(idx, ty) => {
700+
stable_mir::mir::ProjectionElem::Field(idx.stable(tables), ty.stable(tables))
701+
}
702+
Index(local) => stable_mir::mir::ProjectionElem::Index(local.stable(tables)),
703+
ConstantIndex { offset, min_length, from_end } => {
704+
stable_mir::mir::ProjectionElem::ConstantIndex {
705+
offset: *offset,
706+
min_length: *min_length,
707+
from_end: *from_end,
708+
}
709+
}
710+
Subslice { from, to, from_end } => stable_mir::mir::ProjectionElem::Subslice {
711+
from: *from,
712+
to: *to,
713+
from_end: *from_end,
714+
},
715+
// MIR includes an `Option<Symbol>` argument for `Downcast` that is the name of the
716+
// variant, used for printing MIR. However this information should also be accessible
717+
// via a lookup using the `VariantIdx`. The `Option<Symbol>` argument is therefore
718+
// dropped when converting to Stable MIR. A brief justification for this decision can be
719+
// found at https://github.com/rust-lang/rust/pull/117517#issuecomment-1811683486
720+
Downcast(_, idx) => stable_mir::mir::ProjectionElem::Downcast(idx.stable(tables)),
721+
OpaqueCast(ty) => stable_mir::mir::ProjectionElem::OpaqueCast(ty.stable(tables)),
722+
Subtype(ty) => stable_mir::mir::ProjectionElem::Subtype(ty.stable(tables)),
689723
}
690724
}
691725
}
692726

693727
impl<'tcx> Stable<'tcx> for mir::UserTypeProjection {
694728
type T = stable_mir::mir::UserTypeProjection;
695729

696-
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
697-
UserTypeProjection { base: self.base.as_usize(), projection: format!("{:?}", self.projs) }
730+
fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T {
731+
UserTypeProjection { base: self.base.as_usize(), projection: opaque(&self.projs) }
698732
}
699733
}
700734

compiler/stable_mir/src/mir/body.rs

+112-2
Original file line numberDiff line numberDiff line change
@@ -398,22 +398,128 @@ pub enum Operand {
398398
pub struct Place {
399399
pub local: Local,
400400
/// projection out of a place (access a field, deref a pointer, etc)
401-
pub projection: String,
401+
pub projection: Vec<ProjectionElem>,
402+
}
403+
404+
// In MIR ProjectionElem is parameterized on the second Field argument and the Index argument. This
405+
// is so it can be used for both Places (for which the projection elements are of type
406+
// ProjectionElem<Local, Ty>) and user-provided type annotations (for which the projection elements
407+
// are of type ProjectionElem<(), ()>). In SMIR we don't need this generality, so we just use
408+
// ProjectionElem for Places.
409+
#[derive(Clone, Debug, Eq, PartialEq)]
410+
pub enum ProjectionElem {
411+
/// Dereference projections (e.g. `*_1`) project to the address referenced by the base place.
412+
Deref,
413+
414+
/// A field projection (e.g., `f` in `_1.f`) project to a field in the base place. The field is
415+
/// referenced by source-order index rather than the name of the field. The fields type is also
416+
/// given.
417+
Field(FieldIdx, Ty),
418+
419+
/// Index into a slice/array. The value of the index is computed at runtime using the `V`
420+
/// argument.
421+
///
422+
/// Note that this does not also dereference, and so it does not exactly correspond to slice
423+
/// indexing in Rust. In other words, in the below Rust code:
424+
///
425+
/// ```rust
426+
/// let x = &[1, 2, 3, 4];
427+
/// let i = 2;
428+
/// x[i];
429+
/// ```
430+
///
431+
/// The `x[i]` is turned into a `Deref` followed by an `Index`, not just an `Index`. The same
432+
/// thing is true of the `ConstantIndex` and `Subslice` projections below.
433+
Index(Local),
434+
435+
/// Index into a slice/array given by offsets.
436+
///
437+
/// These indices are generated by slice patterns. Easiest to explain by example:
438+
///
439+
/// ```ignore (illustrative)
440+
/// [X, _, .._, _, _] => { offset: 0, min_length: 4, from_end: false },
441+
/// [_, X, .._, _, _] => { offset: 1, min_length: 4, from_end: false },
442+
/// [_, _, .._, X, _] => { offset: 2, min_length: 4, from_end: true },
443+
/// [_, _, .._, _, X] => { offset: 1, min_length: 4, from_end: true },
444+
/// ```
445+
ConstantIndex {
446+
/// index or -index (in Python terms), depending on from_end
447+
offset: u64,
448+
/// The thing being indexed must be at least this long. For arrays this
449+
/// is always the exact length.
450+
min_length: u64,
451+
/// Counting backwards from end? This is always false when indexing an
452+
/// array.
453+
from_end: bool,
454+
},
455+
456+
/// Projects a slice from the base place.
457+
///
458+
/// These indices are generated by slice patterns. If `from_end` is true, this represents
459+
/// `slice[from..slice.len() - to]`. Otherwise it represents `array[from..to]`.
460+
Subslice {
461+
from: u64,
462+
to: u64,
463+
/// Whether `to` counts from the start or end of the array/slice.
464+
from_end: bool,
465+
},
466+
467+
/// "Downcast" to a variant of an enum or a coroutine.
468+
Downcast(VariantIdx),
469+
470+
/// Like an explicit cast from an opaque type to a concrete type, but without
471+
/// requiring an intermediate variable.
472+
OpaqueCast(Ty),
473+
474+
/// A `Subtype(T)` projection is applied to any `StatementKind::Assign` where
475+
/// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping
476+
/// explicit during optimizations and codegen.
477+
///
478+
/// This projection doesn't impact the runtime behavior of the program except for potentially changing
479+
/// some type metadata of the interpreter or codegen backend.
480+
Subtype(Ty),
402481
}
403482

404483
#[derive(Clone, Debug, Eq, PartialEq)]
405484
pub struct UserTypeProjection {
406485
pub base: UserTypeAnnotationIndex,
407-
pub projection: String,
486+
487+
pub projection: Opaque,
408488
}
409489

410490
pub type Local = usize;
411491

412492
pub const RETURN_LOCAL: Local = 0;
413493

494+
/// The source-order index of a field in a variant.
495+
///
496+
/// For example, in the following types,
497+
/// ```ignore(illustrative)
498+
/// enum Demo1 {
499+
/// Variant0 { a: bool, b: i32 },
500+
/// Variant1 { c: u8, d: u64 },
501+
/// }
502+
/// struct Demo2 { e: u8, f: u16, g: u8 }
503+
/// ```
504+
/// `a`'s `FieldIdx` is `0`,
505+
/// `b`'s `FieldIdx` is `1`,
506+
/// `c`'s `FieldIdx` is `0`, and
507+
/// `g`'s `FieldIdx` is `2`.
414508
type FieldIdx = usize;
415509

416510
/// The source-order index of a variant in a type.
511+
///
512+
/// For example, in the following types,
513+
/// ```ignore(illustrative)
514+
/// enum Demo1 {
515+
/// Variant0 { a: bool, b: i32 },
516+
/// Variant1 { c: u8, d: u64 },
517+
/// }
518+
/// struct Demo2 { e: u8, f: u16, g: u8 }
519+
/// ```
520+
/// `a` is in the variant with the `VariantIdx` of `0`,
521+
/// `c` is in the variant with the `VariantIdx` of `1`, and
522+
/// `g` is in the variant with the `VariantIdx` of `0`.
417523
pub type VariantIdx = usize;
418524

419525
type UserTypeAnnotationIndex = usize;
@@ -536,6 +642,10 @@ impl Constant {
536642
}
537643

538644
impl Place {
645+
// FIXME(klinvill): This function is expected to resolve down the chain of projections to get
646+
// the type referenced at the end of it. E.g. calling `ty()` on `*(_1.f)` should end up
647+
// returning the type referenced by `f`. The information needed to do this may not currently be
648+
// present in Stable MIR since at least an implementation for AdtDef is probably needed.
539649
pub fn ty(&self, locals: &[LocalDecl]) -> Ty {
540650
let _start_ty = locals[self.local].ty;
541651
todo!("Implement projection")

compiler/stable_mir/src/mir/visit.rs

+32-1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,15 @@ pub trait MirVisitor {
7676
self.super_place(place, ptx, location)
7777
}
7878

79+
fn visit_projection_elem(
80+
&mut self,
81+
elem: &ProjectionElem,
82+
ptx: PlaceContext,
83+
location: Location,
84+
) {
85+
self.super_projection_elem(elem, ptx, location);
86+
}
87+
7988
fn visit_local(&mut self, local: &Local, ptx: PlaceContext, location: Location) {
8089
let _ = (local, ptx, location);
8190
}
@@ -264,7 +273,29 @@ pub trait MirVisitor {
264273
fn super_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) {
265274
let _ = location;
266275
let _ = ptx;
267-
visit_opaque(&Opaque(place.projection.clone()));
276+
self.visit_local(&place.local, ptx, location);
277+
278+
for elem in &place.projection {
279+
self.visit_projection_elem(elem, ptx, location);
280+
}
281+
}
282+
283+
fn super_projection_elem(
284+
&mut self,
285+
elem: &ProjectionElem,
286+
ptx: PlaceContext,
287+
location: Location,
288+
) {
289+
match elem {
290+
ProjectionElem::Deref => {}
291+
ProjectionElem::Field(_idx, ty) => self.visit_ty(ty, location),
292+
ProjectionElem::Index(local) => self.visit_local(local, ptx, location),
293+
ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } => {}
294+
ProjectionElem::Subslice { from: _, to: _, from_end: _ } => {}
295+
ProjectionElem::Downcast(_idx) => {}
296+
ProjectionElem::OpaqueCast(ty) => self.visit_ty(ty, location),
297+
ProjectionElem::Subtype(ty) => self.visit_ty(ty, location),
298+
}
268299
}
269300

270301
fn super_rvalue(&mut self, rvalue: &Rvalue, location: Location) {

0 commit comments

Comments
 (0)