Auto merge of #105356 - JakobDegen:more-custom-mir, r=oli-obk

Custom MIR: Many more improvements

Commits are each atomic changes, best reviewed one at a time, with the exception that the last commit includes all the documentation.

### First commit

Unsafetyck was not correctly disabled before for `dialect = "built"` custom MIR. This is fixed and a regression test is added.

### Second commit

Implements `Discriminant`, `SetDiscriminant`, and `SwitchInt`.

### Third commit

Implements indexing, field, and variant projections.

### Fourth commit

Documents the previous commits and everything else.

There is some amount of weirdness here due to having to beat Rust syntax into cooperating with MIR concepts, but it hopefully should not be too much. All of it is documented.

r? `@oli-obk`
This commit is contained in:
bors 2022-12-15 19:59:48 +00:00
commit ec56537c43
20 changed files with 731 additions and 32 deletions

View file

@ -533,6 +533,11 @@ impl<'tcx> Body<'tcx> {
};
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)]

View file

@ -25,7 +25,7 @@ use rustc_index::vec::IndexVec;
use rustc_middle::{
mir::*,
thir::*,
ty::{Ty, TyCtxt},
ty::{ParamEnv, Ty, TyCtxt},
};
use rustc_span::Span;
@ -78,6 +78,7 @@ pub(super) fn build_custom_mir<'tcx>(
let mut pctxt = ParseCtxt {
tcx,
param_env: tcx.param_env(did),
thir,
source_scope: OUTERMOST_SOURCE_SCOPE,
body: &mut body,
@ -132,6 +133,7 @@ fn parse_attribute(attr: &Attribute) -> MirPhase {
struct ParseCtxt<'tcx, 'body> {
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
thir: &'body Thir<'tcx>,
source_scope: SourceScope,

View file

@ -1,5 +1,11 @@
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};
@ -12,6 +18,14 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
@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)?;
@ -21,18 +35,60 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
}
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)?)
),
@ -55,7 +111,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
| 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),
@ -63,12 +119,42 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
}
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> {
@ -102,4 +188,16 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
},
)
}
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())
}),
)
}
}

View file

@ -500,7 +500,7 @@ fn unsafety_check_result<'tcx>(
// `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(),

View file

@ -21,15 +21,14 @@
//! #[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()
@ -38,22 +37,168 @@
//! }
//! ```
//!
//! 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(
@ -69,9 +214,10 @@
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!() }
}
}
@ -82,8 +228,73 @@ define!("mir_retag_raw", fn RetagRaw<T>(place: T));
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.
@ -139,6 +350,13 @@ pub macro mir {
}}
}
/// 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

View file

@ -0,0 +1,120 @@
#![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));
}

View file

@ -0,0 +1,10 @@
// 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
}
}

View file

@ -0,0 +1,10 @@
// 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
}
}

View file

@ -0,0 +1,19 @@
// 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
}
}

View file

@ -0,0 +1,21 @@
// 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
}
}

View file

@ -0,0 +1,21 @@
// 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
}
}

View file

@ -0,0 +1,85 @@
#![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);
}

View file

@ -0,0 +1,10 @@
// 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
}
}

View file

@ -0,0 +1,13 @@
// 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
}
}

View file

@ -0,0 +1,13 @@
// 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
}
}

View file

@ -0,0 +1,10 @@
// 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
}
}

View file

@ -0,0 +1,10 @@
// 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
}
}

View file

@ -0,0 +1,10 @@
// 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
}
}

View file

@ -0,0 +1,10 @@
// 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
}
}

View file

@ -36,8 +36,22 @@ pub fn immut_ref(x: &i32) -> &i32 {
)
}
// 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);
}
}