summaryrefslogtreecommitdiffstats
path: root/library/core/src/intrinsics
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--library/core/src/intrinsics.rs69
-rw-r--r--library/core/src/intrinsics/mir.rs302
2 files changed, 283 insertions, 88 deletions
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index 7ed7d767f..a315a28fb 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -55,7 +55,6 @@
#![allow(missing_docs)]
use crate::marker::DiscriminantKind;
-#[cfg(not(bootstrap))]
use crate::marker::Tuple;
use crate::mem;
@@ -959,13 +958,13 @@ extern "rust-intrinsic" {
#[rustc_safe_intrinsic]
pub fn assert_zero_valid<T>();
- /// A guard for unsafe functions that cannot ever be executed if `T` has invalid
- /// bit patterns: This will statically either panic, or do nothing.
+ /// A guard for `std::mem::uninitialized`. This will statically either panic, or do nothing.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_const_unstable(feature = "const_assert_type2", issue = "none")]
#[rustc_safe_intrinsic]
- pub fn assert_uninit_valid<T>();
+ #[cfg(not(bootstrap))]
+ pub fn assert_mem_uninitialized_valid<T>();
/// Gets a reference to a static `Location` indicating where it was called.
///
@@ -2175,66 +2174,6 @@ extern "rust-intrinsic" {
/// `unreachable_unchecked` is actually being reached. The bug is in *crate A*,
/// which violates the principle that a `const fn` must behave the same at
/// compile-time and at run-time. The unsafe code in crate B is fine.
- #[cfg(bootstrap)]
- #[rustc_const_unstable(feature = "const_eval_select", issue = "none")]
- pub fn const_eval_select<ARG, F, G, RET>(arg: ARG, called_in_const: F, called_at_rt: G) -> RET
- where
- G: FnOnce<ARG, Output = RET>,
- F: FnOnce<ARG, Output = RET>;
-
- /// Selects which function to call depending on the context.
- ///
- /// If this function is evaluated at compile-time, then a call to this
- /// intrinsic will be replaced with a call to `called_in_const`. It gets
- /// replaced with a call to `called_at_rt` otherwise.
- ///
- /// # Type Requirements
- ///
- /// The two functions must be both function items. They cannot be function
- /// pointers or closures. The first function must be a `const fn`.
- ///
- /// `arg` will be the tupled arguments that will be passed to either one of
- /// the two functions, therefore, both functions must accept the same type of
- /// arguments. Both functions must return RET.
- ///
- /// # Safety
- ///
- /// The two functions must behave observably equivalent. Safe code in other
- /// crates may assume that calling a `const fn` at compile-time and at run-time
- /// produces the same result. A function that produces a different result when
- /// evaluated at run-time, or has any other observable side-effects, is
- /// *unsound*.
- ///
- /// Here is an example of how this could cause a problem:
- /// ```no_run
- /// #![feature(const_eval_select)]
- /// #![feature(core_intrinsics)]
- /// use std::hint::unreachable_unchecked;
- /// use std::intrinsics::const_eval_select;
- ///
- /// // Crate A
- /// pub const fn inconsistent() -> i32 {
- /// fn runtime() -> i32 { 1 }
- /// const fn compiletime() -> i32 { 2 }
- ///
- /// unsafe {
- // // ⚠ This code violates the required equivalence of `compiletime`
- /// // and `runtime`.
- /// const_eval_select((), compiletime, runtime)
- /// }
- /// }
- ///
- /// // Crate B
- /// const X: i32 = inconsistent();
- /// let x = inconsistent();
- /// if x != X { unsafe { unreachable_unchecked(); }}
- /// ```
- ///
- /// This code causes Undefined Behavior when being run, since the
- /// `unreachable_unchecked` is actually being reached. The bug is in *crate A*,
- /// which violates the principle that a `const fn` must behave the same at
- /// compile-time and at run-time. The unsafe code in crate B is fine.
- #[cfg(not(bootstrap))]
#[rustc_const_unstable(feature = "const_eval_select", issue = "none")]
pub fn const_eval_select<ARG: Tuple, F, G, RET>(
arg: ARG,
@@ -2281,7 +2220,7 @@ macro_rules! assert_unsafe_precondition {
fn runtime$(<$($tt)*>)?($($i:$ty),*) {
if !$e {
// don't unwind to reduce impact on code size
- ::core::panicking::panic_str_nounwind(
+ ::core::panicking::panic_nounwind(
concat!("unsafe precondition(s) violated: ", $name)
);
}
diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs
index 8ba1c1228..e3157b669 100644
--- a/library/core/src/intrinsics/mir.rs
+++ b/library/core/src/intrinsics/mir.rs
@@ -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,200 @@
//! }
//! ```
//!
-//! 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
+//!
+#![cfg_attr(bootstrap, doc = "```rust,compile_fail")]
+#![cfg_attr(not(bootstrap), doc = "```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()
+//! })
+//! }
+//!
+//! #[custom_mir(dialect = "runtime", phase = "optimized")]
+//! fn push_and_pop<T>(v: &mut Vec<T>, value: T) {
+//! mir!(
+//! let unused;
+//! let popped;
+//!
+//! {
+//! Call(unused, pop, Vec::push(v, value))
+//! }
+//!
+//! pop = {
+//! Call(popped, drop, Vec::pop(v))
+//! }
+//!
+//! drop = {
+//! Drop(popped, ret)
+//! }
+//!
+//! ret = {
+//! 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
+//!
+//! Custom MIR does not currently support cleanup blocks or non-trivial unwind paths. As such, there
+//! are no resume and abort terminators, and terminators that might unwind do not have any way to
+//! indicate the unwind block.
+//!
+//! - [`Goto`], [`Return`], [`Unreachable`], [`Drop`](Drop()), and [`DropAndReplace`] 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.
+//! - [`Call`] has an associated function as well. The third argument of this function is a normal
+//! function call expresion, for example `my_other_function(a, 5)`.
//!
#![unstable(
@@ -69,21 +246,93 @@
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_return", fn Return() -> BasicBlock);
define!("mir_goto", fn Goto(destination: BasicBlock) -> BasicBlock);
+define!("mir_unreachable", fn Unreachable() -> BasicBlock);
+define!("mir_drop", fn Drop<T>(place: T, goto: BasicBlock));
+define!("mir_drop_and_replace", fn DropAndReplace<T>(place: T, value: T, goto: BasicBlock));
+define!("mir_call", fn Call<T>(place: T, goto: BasicBlock, call: T));
+define!("mir_storage_live", fn StorageLive<T>(local: T));
+define!("mir_storage_dead", fn StorageDead<T>(local: T));
define!("mir_retag", fn Retag<T>(place: T));
-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
+ ///
+ #[cfg_attr(bootstrap, doc = "```rust,compile_fail")]
+ #[cfg_attr(not(bootstrap), doc = "```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 +388,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