};
injection_phase > self.phase
}
+
+ #[inline]
+ pub fn is_custom_mir(&self) -> bool {
+ self.injection_phase.is_some()
+ }
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, TyEncodable, TyDecodable, HashStable)]
use rustc_middle::{
mir::*,
thir::*,
- ty::{Ty, TyCtxt},
+ ty::{ParamEnv, Ty, TyCtxt},
};
use rustc_span::Span;
let mut pctxt = ParseCtxt {
tcx,
+ param_env: tcx.param_env(did),
thir,
source_scope: OUTERMOST_SOURCE_SCOPE,
body: &mut body,
struct ParseCtxt<'tcx, 'body> {
tcx: TyCtxt<'tcx>,
+ param_env: ParamEnv<'tcx>,
thir: &'body Thir<'tcx>,
source_scope: SourceScope,
use rustc_middle::mir::interpret::{ConstValue, Scalar};
+use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::{mir::*, thir::*, ty};
+use rustc_span::Span;
+use rustc_target::abi::VariantIdx;
+
+use crate::build::custom::ParseError;
+use crate::build::expr::as_constant::as_constant_inner;
use super::{parse_by_kind, PResult, ParseCtxt};
@call("mir_retag_raw", args) => {
Ok(StatementKind::Retag(RetagKind::Raw, Box::new(self.parse_place(args[0])?)))
},
+ @call("mir_set_discriminant", args) => {
+ let place = self.parse_place(args[0])?;
+ let var = self.parse_integer_literal(args[1])? as u32;
+ Ok(StatementKind::SetDiscriminant {
+ place: Box::new(place),
+ variant_index: VariantIdx::from_u32(var),
+ })
+ },
ExprKind::Assign { lhs, rhs } => {
let lhs = self.parse_place(*lhs)?;
let rhs = self.parse_rvalue(*rhs)?;
}
pub fn parse_terminator(&self, expr_id: ExprId) -> PResult<TerminatorKind<'tcx>> {
- parse_by_kind!(self, expr_id, _, "terminator",
+ parse_by_kind!(self, expr_id, expr, "terminator",
@call("mir_return", _args) => {
Ok(TerminatorKind::Return)
},
@call("mir_goto", args) => {
Ok(TerminatorKind::Goto { target: self.parse_block(args[0])? } )
},
+ ExprKind::Match { scrutinee, arms } => {
+ let discr = self.parse_operand(*scrutinee)?;
+ self.parse_match(arms, expr.span).map(|t| TerminatorKind::SwitchInt { discr, targets: t })
+ },
)
}
+ fn parse_match(&self, arms: &[ArmId], span: Span) -> PResult<SwitchTargets> {
+ let Some((otherwise, rest)) = arms.split_last() else {
+ return Err(ParseError {
+ span,
+ item_description: format!("no arms"),
+ expected: "at least one arm".to_string(),
+ })
+ };
+
+ let otherwise = &self.thir[*otherwise];
+ let PatKind::Wild = otherwise.pattern.kind else {
+ return Err(ParseError {
+ span: otherwise.span,
+ item_description: format!("{:?}", otherwise.pattern.kind),
+ expected: "wildcard pattern".to_string(),
+ })
+ };
+ let otherwise = self.parse_block(otherwise.body)?;
+
+ let mut values = Vec::new();
+ let mut targets = Vec::new();
+ for arm in rest {
+ let arm = &self.thir[*arm];
+ let PatKind::Constant { value } = arm.pattern.kind else {
+ return Err(ParseError {
+ span: arm.pattern.span,
+ item_description: format!("{:?}", arm.pattern.kind),
+ expected: "constant pattern".to_string(),
+ })
+ };
+ values.push(value.eval_bits(self.tcx, self.param_env, arm.pattern.ty));
+ targets.push(self.parse_block(arm.body)?);
+ }
+
+ Ok(SwitchTargets::new(values.into_iter().zip(targets), otherwise))
+ }
+
fn parse_rvalue(&self, expr_id: ExprId) -> PResult<Rvalue<'tcx>> {
parse_by_kind!(self, expr_id, _, "rvalue",
+ @call("mir_discriminant", args) => self.parse_place(args[0]).map(Rvalue::Discriminant),
ExprKind::Borrow { borrow_kind, arg } => Ok(
Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?)
),
| ExprKind::ConstParam { .. }
| ExprKind::ConstBlock { .. } => {
Ok(Operand::Constant(Box::new(
- crate::build::expr::as_constant::as_constant_inner(expr, |_| None, self.tcx)
+ as_constant_inner(expr, |_| None, self.tcx)
)))
},
_ => self.parse_place(expr_id).map(Operand::Copy),
}
fn parse_place(&self, expr_id: ExprId) -> PResult<Place<'tcx>> {
- parse_by_kind!(self, expr_id, _, "place",
- ExprKind::Deref { arg } => Ok(
- self.parse_place(*arg)?.project_deeper(&[PlaceElem::Deref], self.tcx)
- ),
- _ => self.parse_local(expr_id).map(Place::from),
- )
+ self.parse_place_inner(expr_id).map(|(x, _)| x)
+ }
+
+ fn parse_place_inner(&self, expr_id: ExprId) -> PResult<(Place<'tcx>, PlaceTy<'tcx>)> {
+ let (parent, proj) = parse_by_kind!(self, expr_id, expr, "place",
+ @call("mir_field", args) => {
+ let (parent, ty) = self.parse_place_inner(args[0])?;
+ let field = Field::from_u32(self.parse_integer_literal(args[1])? as u32);
+ let field_ty = ty.field_ty(self.tcx, field);
+ let proj = PlaceElem::Field(field, field_ty);
+ let place = parent.project_deeper(&[proj], self.tcx);
+ return Ok((place, PlaceTy::from_ty(field_ty)));
+ },
+ @call("mir_variant", args) => {
+ (args[0], PlaceElem::Downcast(
+ None,
+ VariantIdx::from_u32(self.parse_integer_literal(args[1])? as u32)
+ ))
+ },
+ ExprKind::Deref { arg } => {
+ parse_by_kind!(self, *arg, _, "does not matter",
+ @call("mir_make_place", args) => return self.parse_place_inner(args[0]),
+ _ => (*arg, PlaceElem::Deref),
+ )
+ },
+ ExprKind::Index { lhs, index } => (*lhs, PlaceElem::Index(self.parse_local(*index)?)),
+ ExprKind::Field { lhs, name: field, .. } => (*lhs, PlaceElem::Field(*field, expr.ty)),
+ _ => {
+ let place = self.parse_local(expr_id).map(Place::from)?;
+ return Ok((place, PlaceTy::from_ty(expr.ty)))
+ },
+ );
+ let (parent, ty) = self.parse_place_inner(parent)?;
+ let place = parent.project_deeper(&[proj], self.tcx);
+ let ty = ty.projection_ty(self.tcx, proj);
+ Ok((place, ty))
}
fn parse_local(&self, expr_id: ExprId) -> PResult<Local> {
},
)
}
+
+ fn parse_integer_literal(&self, expr_id: ExprId) -> PResult<u128> {
+ parse_by_kind!(self, expr_id, expr, "constant",
+ ExprKind::Literal { .. }
+ | ExprKind::NamedConst { .. }
+ | ExprKind::NonHirLiteral { .. }
+ | ExprKind::ConstBlock { .. } => Ok({
+ let value = as_constant_inner(expr, |_| None, self.tcx);
+ value.literal.eval_bits(self.tcx, self.param_env, value.ty())
+ }),
+ )
+ }
}
// `mir_built` force this.
let body = &tcx.mir_built(def).borrow();
- if body.should_skip() {
+ if body.is_custom_mir() {
return tcx.arena.alloc(UnsafetyCheckResult {
violations: Vec::new(),
used_unsafe_blocks: FxHashSet::default(),
//! #[custom_mir(dialect = "built")]
//! pub fn simple(x: i32) -> i32 {
//! mir!(
-//! let temp1: i32;
-//! let temp2: _;
+//! let temp2: i32;
//!
//! {
-//! temp1 = x;
-//! Goto(exit)
+//! let temp1 = x;
+//! Goto(my_second_block)
//! }
//!
-//! exit = {
+//! my_second_block = {
//! temp2 = Move(temp1);
//! RET = temp2;
//! Return()
//! }
//! ```
//!
-//! Hopefully most of this is fairly self-explanatory. Expanding on some notable details:
+//! The `custom_mir` attribute tells the compiler to treat the function as being custom MIR. This
+//! attribute only works on functions - there is no way to insert custom MIR into the middle of
+//! another function. The `dialect` and `phase` parameters indicate which [version of MIR][dialect
+//! docs] you are inserting here. Generally you'll want to use `#![custom_mir(dialect = "built")]`
+//! if you want your MIR to be modified by the full MIR pipeline, or `#![custom_mir(dialect =
+//! "runtime", phase = "optimized")] if you don't.
//!
-//! - The `custom_mir` attribute tells the compiler to treat the function as being custom MIR. This
-//! attribute only works on functions - there is no way to insert custom MIR into the middle of
-//! another function.
-//! - The `dialect` and `phase` parameters indicate which version of MIR you are inserting here.
-//! This will normally be the phase that corresponds to the thing you are trying to test. The
-//! phase can be omitted for dialects that have just one.
-//! - You should define your function signature like you normally would. Externally, this function
-//! can be called like any other function.
-//! - Type inference works - you don't have to spell out the type of all of your locals.
+//! [dialect docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.MirPhase.html
//!
-//! For now, all statements and terminators are parsed from nested invocations of the special
-//! functions provided in this module. We additionally want to (but do not yet) support more
-//! "normal" Rust syntax in places where it makes sense. Also, most kinds of instructions are not
-//! supported yet.
+//! The input to the [`mir!`] macro is:
+//!
+//! - A possibly empty list of local declarations. Locals can also be declared inline on
+//! assignments via `let`. Type inference generally works. Shadowing does not.
+//! - A list of basic blocks. The first of these is the start block and is where execution begins.
+//! All blocks other than the start block need to be given a name, so that they can be referred
+//! to later.
+//! - Each block is a list of semicolon terminated statements, followed by a terminator. The
+//! syntax for the various statements and terminators is designed to be as similar as possible
+//! to the syntax for analogous concepts in native Rust. See below for a list.
+//!
+//! # Examples
+//!
+//! ```rust
+//! #![feature(core_intrinsics, custom_mir)]
+//!
+//! extern crate core;
+//! use core::intrinsics::mir::*;
+//!
+//! #[custom_mir(dialect = "built")]
+//! pub fn choose_load(a: &i32, b: &i32, c: bool) -> i32 {
+//! mir!(
+//! {
+//! match c {
+//! true => t,
+//! _ => f,
+//! }
+//! }
+//!
+//! t = {
+//! let temp = a;
+//! Goto(load_and_exit)
+//! }
+//!
+//! f = {
+//! temp = b;
+//! Goto(load_and_exit)
+//! }
+//!
+//! load_and_exit = {
+//! RET = *temp;
+//! Return()
+//! }
+//! )
+//! }
+//!
+//! #[custom_mir(dialect = "built")]
+//! fn unwrap_unchecked<T>(opt: Option<T>) -> T {
+//! mir!({
+//! RET = Move(Field(Variant(opt, 1), 0));
+//! Return()
+//! })
+//! }
+//! ```
+//!
+//! We can also set off compilation failures that happen in sufficiently late stages of the
+//! compiler:
+//!
+//! ```rust,compile_fail
+//! #![feature(core_intrinsics, custom_mir)]
+//!
+//! extern crate core;
+//! use core::intrinsics::mir::*;
+//!
+//! #[custom_mir(dialect = "built")]
+//! fn borrow_error(should_init: bool) -> i32 {
+//! mir!(
+//! let temp: i32;
+//!
+//! {
+//! match should_init {
+//! true => init,
+//! _ => use_temp,
+//! }
+//! }
+//!
+//! init = {
+//! temp = 0;
+//! Goto(use_temp)
+//! }
+//!
+//! use_temp = {
+//! RET = temp;
+//! Return()
+//! }
+//! )
+//! }
+//! ```
+//!
+//! ```text
+//! error[E0381]: used binding is possibly-uninitialized
+//! --> test.rs:24:13
+//! |
+//! 8 | / mir!(
+//! 9 | | let temp: i32;
+//! 10 | |
+//! 11 | | {
+//! ... |
+//! 19 | | temp = 0;
+//! | | -------- binding initialized here in some conditions
+//! ... |
+//! 24 | | RET = temp;
+//! | | ^^^^^^^^^^ value used here but it is possibly-uninitialized
+//! 25 | | Return()
+//! 26 | | }
+//! 27 | | )
+//! | |_____- binding declared here but left uninitialized
+//!
+//! error: aborting due to previous error
+//!
+//! For more information about this error, try `rustc --explain E0381`.
+//! ```
+//!
+//! # Syntax
+//!
+//! The lists below are an exhaustive description of how various MIR constructs can be created.
+//! Anything missing from the list should be assumed to not be supported, PRs welcome.
+//!
+//! #### Locals
+//!
+//! - The `_0` return local can always be accessed via `RET`.
+//! - Arguments can be accessed via their regular name.
+//! - All other locals need to be declared with `let` somewhere and then can be accessed by name.
+//!
+//! #### Places
+//! - Locals implicit convert to places.
+//! - Field accesses, derefs, and indexing work normally.
+//! - Fields in variants can be accessed via the [`Variant`] and [`Field`] associated functions,
+//! see their documentation for details.
+//!
+//! #### Operands
+//! - Places implicitly convert to `Copy` operands.
+//! - `Move` operands can be created via [`Move`].
+//! - Const blocks, literals, named constants, and const params all just work.
+//! - [`Static`] and [`StaticMut`] can be used to create `&T` and `*mut T`s to statics. These are
+//! constants in MIR and the only way to access statics.
+//!
+//! #### Statements
+//! - Assign statements work via normal Rust assignment.
+//! - [`Retag`] statements have an associated function.
+//!
+//! #### Rvalues
+//!
+//! - Operands implicitly convert to `Use` rvalues.
+//! - `&`, `&mut`, `addr_of!`, and `addr_of_mut!` all work to create their associated rvalue.
+//! - [`Discriminant`] has an associated function.
+//!
+//! #### Terminators
+//!
+//! - [`Goto`] and [`Return`] have associated functions.
+//! - `match some_int_operand` becomes a `SwitchInt`. Each arm should be `literal => basic_block`
+//! - The exception is the last arm, which must be `_ => basic_block` and corresponds to the
+//! otherwise branch.
//!
#![unstable(
pub struct BasicBlock;
macro_rules! define {
- ($name:literal, $($sig:tt)*) => {
+ ($name:literal, $( #[ $meta:meta ] )* fn $($sig:tt)*) => {
#[rustc_diagnostic_item = $name]
- pub $($sig)* { panic!() }
+ $( #[ $meta ] )*
+ pub fn $($sig)* { panic!() }
}
}
define!("mir_move", fn Move<T>(place: T) -> T);
define!("mir_static", fn Static<T>(s: T) -> &'static T);
define!("mir_static_mut", fn StaticMut<T>(s: T) -> *mut T);
+define!(
+ "mir_discriminant",
+ /// Gets the discriminant of a place.
+ fn Discriminant<T>(place: T) -> <T as ::core::marker::DiscriminantKind>::Discriminant
+);
+define!("mir_set_discriminant", fn SetDiscriminant<T>(place: T, index: u32));
+define!(
+ "mir_field",
+ /// Access the field with the given index of some place.
+ ///
+ /// This only makes sense to use in conjunction with [`Variant`]. If the type you are looking to
+ /// access the field of does not have variants, you can use normal field projection syntax.
+ ///
+ /// There is no proper way to do a place projection to a variant in Rust, and so these two
+ /// functions are a workaround. You can access a field of a variant via `Field(Variant(place,
+ /// var_idx), field_idx)`, where `var_idx` and `field_idx` are appropriate literals. Some
+ /// caveats:
+ ///
+ /// - The return type of `Variant` is always `()`. Don't worry about that, the correct MIR will
+ /// still be generated.
+ /// - In some situations, the return type of `Field` cannot be inferred. You may need to
+ /// annotate it on the function in these cases.
+ /// - Since `Field` is a function call which is not a place expression, using this on the left
+ /// hand side of an expression is rejected by the compiler. [`place!`] is a macro provided to
+ /// work around that issue. Wrap the left hand side of an assignment in the macro to convince
+ /// the compiler that it's ok.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// #![feature(custom_mir, core_intrinsics)]
+ ///
+ /// extern crate core;
+ /// use core::intrinsics::mir::*;
+ ///
+ /// #[custom_mir(dialect = "built")]
+ /// fn unwrap_deref(opt: Option<&i32>) -> i32 {
+ /// mir!({
+ /// RET = *Field::<&i32>(Variant(opt, 1), 0);
+ /// Return()
+ /// })
+ /// }
+ ///
+ /// #[custom_mir(dialect = "built")]
+ /// fn set(opt: &mut Option<i32>) {
+ /// mir!({
+ /// place!(Field(Variant(*opt, 1), 0)) = 5;
+ /// Return()
+ /// })
+ /// }
+ /// ```
+ fn Field<F>(place: (), field: u32) -> F
+);
+define!(
+ "mir_variant",
+ /// Adds a variant projection with the given index to the place.
+ ///
+ /// See [`Field`] for documentation.
+ fn Variant<T>(place: T, index: u32) -> ()
+);
+define!(
+ "mir_make_place",
+ #[doc(hidden)]
+ fn __internal_make_place<T>(place: T) -> *mut T
+);
-/// Convenience macro for generating custom MIR.
+/// Macro for generating custom MIR.
///
/// See the module documentation for syntax details. This macro is not magic - it only transforms
/// your MIR into something that is easier to parse in the compiler.
}}
}
+/// Helper macro that allows you to treat a value expression like a place expression.
+///
+/// See the documentation on [`Variant`] for why this is necessary and how to use it.
+pub macro place($e:expr) {
+ (*::core::intrinsics::mir::__internal_make_place($e))
+}
+
/// Helper macro that extracts the `let` declarations out of a bunch of statements.
///
/// This macro is written using the "statement muncher" strategy. Each invocation parses the first
--- /dev/null
+#![feature(custom_mir, core_intrinsics)]
+
+extern crate core;
+use core::intrinsics::mir::*;
+
+// EMIT_MIR enums.switch_bool.built.after.mir
+#[custom_mir(dialect = "built")]
+pub fn switch_bool(b: bool) -> u32 {
+ mir!(
+ {
+ match b {
+ true => t,
+ false => f,
+ _ => f,
+ }
+ }
+
+ t = {
+ RET = 5;
+ Return()
+ }
+
+ f = {
+ RET = 10;
+ Return()
+ }
+ )
+}
+
+// EMIT_MIR enums.switch_option.built.after.mir
+#[custom_mir(dialect = "built")]
+pub fn switch_option(option: Option<()>) -> bool {
+ mir!(
+ {
+ let discr = Discriminant(option);
+ match discr {
+ 0 => n,
+ 1 => s,
+ _ => s,
+ }
+ }
+
+ n = {
+ RET = false;
+ Return()
+ }
+
+ s = {
+ RET = true;
+ Return()
+ }
+ )
+}
+
+#[repr(u8)]
+enum Bool {
+ False = 0,
+ True = 1,
+}
+
+// EMIT_MIR enums.switch_option_repr.built.after.mir
+#[custom_mir(dialect = "built")]
+fn switch_option_repr(option: Bool) -> bool {
+ mir!(
+ {
+ let discr = Discriminant(option);
+ match discr {
+ 0 => f,
+ _ => t,
+ }
+ }
+
+ t = {
+ RET = true;
+ Return()
+ }
+
+ f = {
+ RET = false;
+ Return()
+ }
+ )
+}
+
+// EMIT_MIR enums.set_discr.built.after.mir
+#[custom_mir(dialect = "runtime", phase = "initial")]
+fn set_discr(option: &mut Option<()>) {
+ mir!({
+ SetDiscriminant(*option, 0);
+ Return()
+ })
+}
+
+// EMIT_MIR enums.set_discr_repr.built.after.mir
+#[custom_mir(dialect = "runtime", phase = "initial")]
+fn set_discr_repr(b: &mut Bool) {
+ mir!({
+ SetDiscriminant(*b, 0);
+ Return()
+ })
+}
+
+fn main() {
+ assert_eq!(switch_bool(true), 5);
+ assert_eq!(switch_bool(false), 10);
+
+ assert_eq!(switch_option(Some(())), true);
+ assert_eq!(switch_option(None), false);
+
+ assert_eq!(switch_option_repr(Bool::True), true);
+ assert_eq!(switch_option_repr(Bool::False), false);
+
+ let mut opt = Some(());
+ set_discr(&mut opt);
+ assert_eq!(opt, None);
+
+ let mut b = Bool::True;
+ set_discr_repr(&mut b);
+ assert!(matches!(b, Bool::False));
+}
--- /dev/null
+// MIR for `set_discr` after built
+
+fn set_discr(_1: &mut Option<()>) -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/enums.rs:+0:39: +0:39
+
+ bb0: {
+ discriminant((*_1)) = 0; // scope 0 at $DIR/enums.rs:+2:9: +2:36
+ return; // scope 0 at $DIR/enums.rs:+3:9: +3:17
+ }
+}
--- /dev/null
+// MIR for `set_discr_repr` after built
+
+fn set_discr_repr(_1: &mut Bool) -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/enums.rs:+0:33: +0:33
+
+ bb0: {
+ discriminant((*_1)) = 0; // scope 0 at $DIR/enums.rs:+2:9: +2:31
+ return; // scope 0 at $DIR/enums.rs:+3:9: +3:17
+ }
+}
--- /dev/null
+// MIR for `switch_bool` after built
+
+fn switch_bool(_1: bool) -> u32 {
+ let mut _0: u32; // return place in scope 0 at $DIR/enums.rs:+0:32: +0:35
+
+ bb0: {
+ switchInt(_1) -> [1: bb1, 0: bb2, otherwise: bb2]; // scope 0 at $DIR/enums.rs:+3:13: +7:14
+ }
+
+ bb1: {
+ _0 = const 5_u32; // scope 0 at $DIR/enums.rs:+11:13: +11:20
+ return; // scope 0 at $DIR/enums.rs:+12:13: +12:21
+ }
+
+ bb2: {
+ _0 = const 10_u32; // scope 0 at $DIR/enums.rs:+16:13: +16:21
+ return; // scope 0 at $DIR/enums.rs:+17:13: +17:21
+ }
+}
--- /dev/null
+// MIR for `switch_option` after built
+
+fn switch_option(_1: Option<()>) -> bool {
+ let mut _0: bool; // return place in scope 0 at $DIR/enums.rs:+0:45: +0:49
+ let mut _2: isize; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+
+ bb0: {
+ _2 = discriminant(_1); // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ switchInt(_2) -> [0: bb1, 1: bb2, otherwise: bb2]; // scope 0 at $DIR/enums.rs:+4:13: +8:14
+ }
+
+ bb1: {
+ _0 = const false; // scope 0 at $DIR/enums.rs:+12:13: +12:24
+ return; // scope 0 at $DIR/enums.rs:+13:13: +13:21
+ }
+
+ bb2: {
+ _0 = const true; // scope 0 at $DIR/enums.rs:+17:13: +17:23
+ return; // scope 0 at $DIR/enums.rs:+18:13: +18:21
+ }
+}
--- /dev/null
+// MIR for `switch_option_repr` after built
+
+fn switch_option_repr(_1: Bool) -> bool {
+ let mut _0: bool; // return place in scope 0 at $DIR/enums.rs:+0:40: +0:44
+ let mut _2: u8; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+
+ bb0: {
+ _2 = discriminant(_1); // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ switchInt(_2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/enums.rs:+4:13: +7:14
+ }
+
+ bb1: {
+ _0 = const true; // scope 0 at $DIR/enums.rs:+11:13: +11:23
+ return; // scope 0 at $DIR/enums.rs:+12:13: +12:21
+ }
+
+ bb2: {
+ _0 = const false; // scope 0 at $DIR/enums.rs:+16:13: +16:24
+ return; // scope 0 at $DIR/enums.rs:+17:13: +17:21
+ }
+}
--- /dev/null
+#![feature(custom_mir, core_intrinsics)]
+
+extern crate core;
+use core::intrinsics::mir::*;
+
+union U {
+ a: i32,
+ b: u32,
+}
+
+// EMIT_MIR projections.unions.built.after.mir
+#[custom_mir(dialect = "built")]
+fn unions(u: U) -> i32 {
+ mir!({
+ RET = u.a;
+ Return()
+ })
+}
+
+// EMIT_MIR projections.tuples.built.after.mir
+#[custom_mir(dialect = "analysis", phase = "post-cleanup")]
+fn tuples(i: (u32, i32)) -> (u32, i32) {
+ mir!(
+ // FIXME(JakobDegen): This is necessary because we can't give type hints for `RET`
+ let temp: (u32, i32);
+ {
+ temp.0 = i.0;
+ temp.1 = i.1;
+
+ RET = temp;
+ Return()
+ }
+ )
+}
+
+// EMIT_MIR projections.unwrap.built.after.mir
+#[custom_mir(dialect = "built")]
+fn unwrap(opt: Option<i32>) -> i32 {
+ mir!({
+ RET = Field(Variant(opt, 1), 0);
+ Return()
+ })
+}
+
+// EMIT_MIR projections.unwrap_deref.built.after.mir
+#[custom_mir(dialect = "built")]
+fn unwrap_deref(opt: Option<&i32>) -> i32 {
+ mir!({
+ RET = *Field::<&i32>(Variant(opt, 1), 0);
+ Return()
+ })
+}
+
+// EMIT_MIR projections.set.built.after.mir
+#[custom_mir(dialect = "built")]
+fn set(opt: &mut Option<i32>) {
+ mir!({
+ place!(Field(Variant(*opt, 1), 0)) = 10;
+ Return()
+ })
+}
+
+// EMIT_MIR projections.simple_index.built.after.mir
+#[custom_mir(dialect = "built")]
+fn simple_index(a: [i32; 10], b: &[i32]) -> i32 {
+ mir!({
+ let temp = 3;
+ RET = a[temp];
+ RET = (*b)[temp];
+ Return()
+ })
+}
+
+fn main() {
+ assert_eq!(unions(U { a: 5 }), 5);
+ assert_eq!(tuples((5, 6)), (5, 6));
+
+ assert_eq!(unwrap(Some(5)), 5);
+ assert_eq!(unwrap_deref(Some(&5)), 5);
+ let mut o = Some(5);
+ set(&mut o);
+ assert_eq!(o, Some(10));
+
+ assert_eq!(simple_index([0; 10], &[0; 10]), 0);
+}
--- /dev/null
+// MIR for `set` after built
+
+fn set(_1: &mut Option<i32>) -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/projections.rs:+0:31: +0:31
+
+ bb0: {
+ (((*_1) as variant#1).0: i32) = const 10_i32; // scope 0 at $DIR/projections.rs:+2:9: +2:48
+ return; // scope 0 at $DIR/projections.rs:+3:9: +3:17
+ }
+}
--- /dev/null
+// MIR for `simple_index` after built
+
+fn simple_index(_1: [i32; 10], _2: &[i32]) -> i32 {
+ let mut _0: i32; // return place in scope 0 at $DIR/projections.rs:+0:45: +0:48
+ let mut _3: usize; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+
+ bb0: {
+ _3 = const 3_usize; // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ _0 = _1[_3]; // scope 0 at $DIR/projections.rs:+3:9: +3:22
+ _0 = (*_2)[_3]; // scope 0 at $DIR/projections.rs:+4:9: +4:25
+ return; // scope 0 at $DIR/projections.rs:+5:9: +5:17
+ }
+}
--- /dev/null
+// MIR for `tuples` after built
+
+fn tuples(_1: (u32, i32)) -> (u32, i32) {
+ let mut _0: (u32, i32); // return place in scope 0 at $DIR/projections.rs:+0:29: +0:39
+ let mut _2: (u32, i32); // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+
+ bb0: {
+ (_2.0: u32) = (_1.0: u32); // scope 0 at $DIR/projections.rs:+5:13: +5:25
+ (_2.1: i32) = (_1.1: i32); // scope 0 at $DIR/projections.rs:+6:13: +6:25
+ _0 = _2; // scope 0 at $DIR/projections.rs:+8:13: +8:23
+ return; // scope 0 at $DIR/projections.rs:+9:13: +9:21
+ }
+}
--- /dev/null
+// MIR for `unions` after built
+
+fn unions(_1: U) -> i32 {
+ let mut _0: i32; // return place in scope 0 at $DIR/projections.rs:+0:20: +0:23
+
+ bb0: {
+ _0 = (_1.0: i32); // scope 0 at $DIR/projections.rs:+2:9: +2:18
+ return; // scope 0 at $DIR/projections.rs:+3:9: +3:17
+ }
+}
--- /dev/null
+// MIR for `unwrap` after built
+
+fn unwrap(_1: Option<i32>) -> i32 {
+ let mut _0: i32; // return place in scope 0 at $DIR/projections.rs:+0:32: +0:35
+
+ bb0: {
+ _0 = ((_1 as variant#1).0: i32); // scope 0 at $DIR/projections.rs:+2:9: +2:40
+ return; // scope 0 at $DIR/projections.rs:+3:9: +3:17
+ }
+}
--- /dev/null
+// MIR for `unwrap_deref` after built
+
+fn unwrap_deref(_1: Option<&i32>) -> i32 {
+ let mut _0: i32; // return place in scope 0 at $DIR/projections.rs:+0:39: +0:42
+
+ bb0: {
+ _0 = (*((_1 as variant#1).0: &i32)); // scope 0 at $DIR/projections.rs:+2:9: +2:49
+ return; // scope 0 at $DIR/projections.rs:+3:9: +3:17
+ }
+}
--- /dev/null
+// MIR for `raw_pointer` after built
+
+fn raw_pointer(_1: *const i32) -> *const i32 {
+ let mut _0: *const i32; // return place in scope 0 at $DIR/references.rs:+0:38: +0:48
+
+ bb0: {
+ _0 = &raw const (*_1); // scope 0 at $DIR/references.rs:+4:9: +4:27
+ return; // scope 0 at $DIR/references.rs:+5:9: +5:17
+ }
+}
)
}
+// EMIT_MIR references.raw_pointer.built.after.mir
+#[custom_mir(dialect = "built")]
+pub fn raw_pointer(x: *const i32) -> *const i32 {
+ // Regression test for a bug in which unsafetyck was not correctly turned off for
+ // `dialect = "built"`
+ mir!({
+ RET = addr_of!(*x);
+ Return()
+ })
+}
+
fn main() {
let mut x = 5;
assert_eq!(*mut_ref(&mut x), 5);
assert_eq!(*immut_ref(&x), 5);
+ unsafe {
+ assert_eq!(*raw_pointer(addr_of!(x)), 5);
+ }
}