env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
(os.pathsep + env["LIBRARY_PATH"]) \
if "LIBRARY_PATH" in env else ""
- env["RUSTFLAGS"] = "-Cdebuginfo=2 "
+ # preserve existing RUSTFLAGS
+ env.setdefault("RUSTFLAGS", "")
+ env["RUSTFLAGS"] += " -Cdebuginfo=2"
build_section = "target.{}".format(self.build_triple())
target_features = []
elif self.get_toml("crt-static", build_section) == "false":
target_features += ["-crt-static"]
if target_features:
- env["RUSTFLAGS"] += "-C target-feature=" + (",".join(target_features)) + " "
+ env["RUSTFLAGS"] += " -C target-feature=" + (",".join(target_features))
target_linker = self.get_toml("linker", build_section)
if target_linker is not None:
- env["RUSTFLAGS"] += "-C linker=" + target_linker + " "
- env["RUSTFLAGS"] += " -Wrust_2018_idioms -Wunused_lifetimes "
+ env["RUSTFLAGS"] += " -C linker=" + target_linker
+ env["RUSTFLAGS"] += " -Wrust_2018_idioms -Wunused_lifetimes"
if self.get_toml("deny-warnings", "rust") != "false":
- env["RUSTFLAGS"] += "-Dwarnings "
+ env["RUSTFLAGS"] += " -Dwarnings"
env["PATH"] = os.path.join(self.bin_root(), "bin") + \
os.pathsep + env["PATH"]
};
Rustc, "src/librustc", true, only_hosts: true, {
builder.ensure(dist::Rustc {
- compiler: self.compiler,
+ compiler: builder.compiler(builder.top_stage, self.target),
});
install_rustc(builder, self.compiler.stage, self.target);
};
|
```
-## plugin-as-library
-
-This lint detects when compiler plugins are used as ordinary library in
-non-plugin crate. Some example code that triggers this lint:
-
-```rust,ignore
-#![feature(plugin)]
-#![plugin(macro_crate_test)]
-
-extern crate macro_crate_test;
-```
-
## private-in-public
This lint detects private items in public interfaces not caught by the old implementation. Some
--- /dev/null
+# `cfg_sanitize`
+
+The tracking issue for this feature is: [#39699]
+
+[#39699]: https://github.com/rust-lang/rust/issues/39699
+
+------------------------
+
+The `cfg_sanitize` feature makes it possible to execute different code
+depending on whether a particular sanitizer is enabled or not.
+
+## Examples
+
+``` rust
+#![feature(cfg_sanitize)]
+
+#[cfg(sanitize = "thread")]
+fn a() {
+ // ...
+}
+
+#[cfg(not(sanitize = "thread"))]
+fn a() {
+ // ...
+}
+
+fn b() {
+ if cfg!(sanitize = "leak") {
+ // ...
+ } else {
+ // ...
+ }
+}
+
+```
+
`rustc_driver::plugin` documentation for more about the
mechanics of defining and loading a plugin.
-If present, arguments passed as `#![plugin(foo(... args ...))]` are not
-interpreted by rustc itself. They are provided to the plugin through the
-`Registry`'s `args` method.
-
In the vast majority of cases, a plugin should *only* be used through
`#![plugin]` and not through an `extern crate` item. Linking a plugin would
pull in all of libsyntax and librustc as dependencies of your crate. This is
-generally unwanted unless you are building another plugin. The
-`plugin_as_library` lint checks these guidelines.
+generally unwanted unless you are building another plugin.
The usual practice is to put compiler plugins in their own crate, separate from
any `macro_rules!` macros or ordinary Rust code meant to be used by consumers
// reference (see #54908).
let layout = Layout::new::<RcBox<()>>()
.extend(value_layout).unwrap().0
- .pad_to_align().unwrap();
+ .pad_to_align();
// Allocate for the layout.
let mem = Global.alloc(layout)
// reference (see #54908).
let layout = Layout::new::<ArcInner<()>>()
.extend(value_layout).unwrap().0
- .pad_to_align().unwrap();
+ .pad_to_align();
let mem = Global.alloc(layout)
.unwrap_or_else(|_| handle_alloc_error(layout));
/// Creates a layout by rounding the size of this layout up to a multiple
/// of the layout's alignment.
///
- /// Returns `Err` if the padded size would overflow.
- ///
/// This is equivalent to adding the result of `padding_needed_for`
/// to the layout's current size.
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
#[inline]
- pub fn pad_to_align(&self) -> Result<Layout, LayoutErr> {
+ pub fn pad_to_align(&self) -> Layout {
let pad = self.padding_needed_for(self.align());
- let new_size = self.size().checked_add(pad)
- .ok_or(LayoutErr { private: () })?;
+ // This cannot overflow. Quoting from the invariant of Layout:
+ // > `size`, when rounded up to the nearest multiple of `align`,
+ // > must not overflow (i.e., the rounded value must be less than
+ // > `usize::MAX`)
+ let new_size = self.size() + pad;
- Layout::from_size_align(new_size, self.align())
+ Layout::from_size_align(new_size, self.align()).unwrap()
}
/// Creates a layout describing the record for `n` instances of
pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
/// Internal hook used by Miri to implement unwinding.
+ /// Compiles to a NOP during non-Miri codegen.
+ ///
/// Perma-unstable: do not use
#[cfg(not(bootstrap))]
- pub fn miri_start_panic(data: *mut (dyn crate::any::Any + crate::marker::Send)) -> !;
+ pub fn miri_start_panic(data: *mut (dyn crate::any::Any + crate::marker::Send)) -> ();
}
// Some functions are defined here because they accidentally got made
/// extracting those success or failure values from an existing instance and
/// creating a new instance from a success or failure value.
#[unstable(feature = "try_trait", issue = "42327")]
-#[rustc_on_unimplemented(
+#[cfg_attr(not(bootstrap), rustc_on_unimplemented(
on(all(
any(from_method="from_error", from_method="from_ok"),
from_desugaring="QuestionMark"),
message="the `?` operator can only be used in {ItemContext} \
that returns `Result` or `Option` \
(or another type that implements `{Try}`)",
-label="cannot use the `?` operator in {ItemContext} that returns `{Self}`"),
+label="cannot use the `?` operator in {ItemContext} that returns `{Self}`",
+enclosing_scope="this function should return `Result` or `Option` to accept `?`"),
on(all(from_method="into_result", from_desugaring="QuestionMark"),
message="the `?` operator can only be applied to values \
that implement `{Try}`",
label="the `?` operator cannot be applied to type `{Self}`")
-)]
+))]
#[doc(alias = "?")]
pub trait Try {
/// The type of this value when viewed as successful.
use core::panic::BoxMeUp;
cfg_if::cfg_if! {
- if #[cfg(miri)] {
- #[path = "miri.rs"]
- mod imp;
- } else if #[cfg(target_os = "emscripten")] {
+ if #[cfg(target_os = "emscripten")] {
#[path = "emcc.rs"]
mod imp;
} else if #[cfg(target_arch = "wasm32")] {
#[unwind(allowed)]
pub unsafe extern "C" fn __rust_start_panic(payload: usize) -> u32 {
let payload = payload as *mut &mut dyn BoxMeUp;
- imp::panic(Box::from_raw((*payload).take_box()))
+ let payload = (*payload).take_box();
+
+ // Miri panic support: cfg'd out of normal builds just to be sure.
+ // When going through normal codegen, `miri_start_panic` is a NOP, so the
+ // Miri-enabled sysroot still supports normal unwinding. But when executed in
+ // Miri, this line initiates unwinding.
+ #[cfg(miri)]
+ core::intrinsics::miri_start_panic(payload);
+
+ imp::panic(Box::from_raw(payload))
}
+++ /dev/null
-#![allow(nonstandard_style)]
-
-use core::any::Any;
-use alloc::boxed::Box;
-
-pub fn payload() -> *mut u8 {
- core::ptr::null_mut()
-}
-
-pub unsafe fn panic(data: Box<dyn Any + Send>) -> ! {
- core::intrinsics::miri_start_panic(Box::into_raw(data))
-}
-
-pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
- Box::from_raw(ptr)
-}
-
-// This is required by the compiler to exist (e.g., it's a lang item),
-// but is never used by Miri. Therefore, we just use a stub here
-#[lang = "eh_personality"]
-#[cfg(not(test))]
-fn rust_eh_personality() {
- unsafe { core::intrinsics::abort() }
-}
-
-// The rest is required on *some* targets to exist (specifically, MSVC targets that use SEH).
-// We just add it on all targets. Copied from `seh.rs`.
-#[repr(C)]
-pub struct _TypeDescriptor {
- pub pVFTable: *const u8,
- pub spare: *mut u8,
- pub name: [u8; 11],
-}
-
-const TYPE_NAME: [u8; 11] = *b"rust_panic\0";
-
-#[cfg_attr(not(test), lang = "eh_catch_typeinfo")]
-static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
- pVFTable: core::ptr::null(),
- spare: core::ptr::null_mut(),
- name: TYPE_NAME,
-};
[] generics: rustc::ty::Generics,
[] trait_def: rustc::ty::TraitDef,
[] adt_def: rustc::ty::AdtDef,
- [] steal_mir: rustc::ty::steal::Steal<rustc::mir::Body<$tcx>>,
- [] mir: rustc::mir::Body<$tcx>,
+ [] steal_mir: rustc::ty::steal::Steal<rustc::mir::BodyCache<$tcx>>,
+ [] mir: rustc::mir::BodyCache<$tcx>,
[] steal_promoted: rustc::ty::steal::Steal<
rustc_index::vec::IndexVec<
rustc::mir::Promoted,
- rustc::mir::Body<$tcx>
+ rustc::mir::BodyCache<$tcx>
>
>,
[] promoted: rustc_index::vec::IndexVec<
rustc::mir::Promoted,
- rustc::mir::Body<$tcx>
+ rustc::mir::BodyCache<$tcx>
>,
[] tables: rustc::ty::TypeckTables<$tcx>,
[] const_allocs: rustc::mir::interpret::Allocation,
AttrKind::Normal(ref item) => {
AttrKind::Normal(AttrItem {
path: item.path.clone(),
- tokens: self.lower_token_stream(item.tokens.clone()),
+ args: self.lower_mac_args(&item.args),
})
}
AttrKind::DocComment(comment) => AttrKind::DocComment(comment)
}
}
+ fn lower_mac_args(&mut self, args: &MacArgs) -> MacArgs {
+ match *args {
+ MacArgs::Empty => MacArgs::Empty,
+ MacArgs::Delimited(dspan, delim, ref tokens) =>
+ MacArgs::Delimited(dspan, delim, self.lower_token_stream(tokens.clone())),
+ MacArgs::Eq(eq_span, ref tokens) =>
+ MacArgs::Eq(eq_span, self.lower_token_stream(tokens.clone())),
+ }
+ }
+
fn lower_token_stream(&mut self, tokens: TokenStream) -> TokenStream {
tokens
.into_trees()
if let ItemKind::MacroDef(ref def) = i.kind {
if !def.legacy || attr::contains_name(&i.attrs, sym::macro_export) {
- let body = self.lower_token_stream(def.stream());
+ let body = self.lower_token_stream(def.body.inner_tokens());
let hir_id = self.lower_node_id(i.id);
self.exported_macros.push(hir::MacroDef {
name: ident.name,
/// This is basically the subset of `Context` that we can
/// build early in the compile pipeline.
pub struct LintStore {
- /// Registered lints. The bool is true if the lint was
- /// added by a plugin.
+ /// Registered lints.
lints: Vec<&'static Lint>,
/// Constructor functions for each variety of lint pass.
use rustc_index::vec::IndexVec;
-use rustc_data_structures::sync::{RwLock, MappedReadGuard, ReadGuard};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
use crate::ich::StableHashingContext;
-use crate::mir::{Body, BasicBlock};
+use crate::mir::{BasicBlock, BasicBlockData, Body, LocalDecls, Location, Successors};
+use rustc_data_structures::graph::{self, GraphPredecessors, GraphSuccessors};
+use rustc_data_structures::graph::dominators::{dominators, Dominators};
+use std::iter;
+use std::ops::{Deref, DerefMut, Index, IndexMut};
+use std::vec::IntoIter;
#[derive(Clone, Debug)]
pub struct Cache {
- predecessors: RwLock<Option<IndexVec<BasicBlock, Vec<BasicBlock>>>>
+ predecessors: Option<IndexVec<BasicBlock, Vec<BasicBlock>>>,
}
-
impl rustc_serialize::Encodable for Cache {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
Encodable::encode(&(), s)
impl Cache {
pub fn new() -> Self {
- Cache {
- predecessors: RwLock::new(None)
+ Self {
+ predecessors: None,
}
}
- pub fn invalidate(&self) {
+ pub fn invalidate_predecessors(&mut self) {
// FIXME: consider being more fine-grained
- *self.predecessors.borrow_mut() = None;
+ self.predecessors = None;
}
- pub fn predecessors(
- &self,
- body: &Body<'_>
- ) -> MappedReadGuard<'_, IndexVec<BasicBlock, Vec<BasicBlock>>> {
- if self.predecessors.borrow().is_none() {
- *self.predecessors.borrow_mut() = Some(calculate_predecessors(body));
+ pub fn ensure_predecessors(&mut self, body: &Body<'_>) {
+ if self.predecessors.is_none() {
+ let mut result = IndexVec::from_elem(vec![], body.basic_blocks());
+ for (bb, data) in body.basic_blocks().iter_enumerated() {
+ if let Some(ref term) = data.terminator {
+ for &tgt in term.successors() {
+ result[tgt].push(bb);
+ }
+ }
+ }
+
+ self.predecessors = Some(result)
}
+ }
+
+ /// This will recompute the predecessors cache if it is not available
+ fn predecessors(&mut self, body: &Body<'_>) -> &IndexVec<BasicBlock, Vec<BasicBlock>> {
+ self.ensure_predecessors(body);
+ self.predecessors.as_ref().unwrap()
+ }
+
+ fn unwrap_predecessors_for(&self, bb: BasicBlock) -> &[BasicBlock] {
+ &self.predecessors.as_ref().unwrap()[bb]
+ }
- ReadGuard::map(self.predecessors.borrow(), |p| p.as_ref().unwrap())
+ fn unwrap_predecessor_locations<'a>(
+ &'a self,
+ loc: Location,
+ body: &'a Body<'a>
+ ) -> impl Iterator<Item = Location> + 'a {
+ let if_zero_locations = if loc.statement_index == 0 {
+ let predecessor_blocks = self.unwrap_predecessors_for(loc.block);
+ let num_predecessor_blocks = predecessor_blocks.len();
+ Some(
+ (0..num_predecessor_blocks)
+ .map(move |i| predecessor_blocks[i])
+ .map(move |bb| body.terminator_loc(bb)),
+ )
+ } else {
+ None
+ };
+
+ let if_not_zero_locations = if loc.statement_index == 0 {
+ None
+ } else {
+ Some(Location { block: loc.block, statement_index: loc.statement_index - 1 })
+ };
+
+ if_zero_locations.into_iter().flatten().chain(if_not_zero_locations)
+ }
+
+ pub fn basic_blocks_mut<'a, 'tcx>(
+ &mut self,
+ body: &'a mut Body<'tcx>
+ ) -> &'a mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
+ debug!("bbm: Clearing predecessors cache for body at: {:?}", body.span.data());
+ self.invalidate_predecessors();
+ &mut body.basic_blocks
+ }
+
+ pub fn basic_blocks_and_local_decls_mut<'a, 'tcx>(
+ &mut self,
+ body: &'a mut Body<'tcx>
+ ) -> (&'a mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &'a mut LocalDecls<'tcx>) {
+ debug!("bbaldm: Clearing predecessors cache for body at: {:?}", body.span.data());
+ self.invalidate_predecessors();
+ (&mut body.basic_blocks, &mut body.local_decls)
}
}
-fn calculate_predecessors(body: &Body<'_>) -> IndexVec<BasicBlock, Vec<BasicBlock>> {
- let mut result = IndexVec::from_elem(vec![], body.basic_blocks());
- for (bb, data) in body.basic_blocks().iter_enumerated() {
- if let Some(ref term) = data.terminator {
- for &tgt in term.successors() {
- result[tgt].push(bb);
- }
+#[derive(Clone, Debug, HashStable, RustcEncodable, RustcDecodable, TypeFoldable)]
+pub struct BodyCache<'tcx> {
+ cache: Cache,
+ body: Body<'tcx>,
+}
+
+impl BodyCache<'tcx> {
+ pub fn new(body: Body<'tcx>) -> Self {
+ Self {
+ cache: Cache::new(),
+ body,
+ }
+ }
+}
+
+#[macro_export]
+macro_rules! read_only {
+ ($body:expr) => {
+ {
+ $body.ensure_predecessors();
+ $body.unwrap_read_only()
+ }
+ };
+}
+
+impl BodyCache<'tcx> {
+ pub fn ensure_predecessors(&mut self) {
+ self.cache.ensure_predecessors(&self.body);
+ }
+
+ pub fn predecessors(&mut self) -> &IndexVec<BasicBlock, Vec<BasicBlock>> {
+ self.cache.predecessors(&self.body)
+ }
+
+ pub fn unwrap_read_only(&self) -> ReadOnlyBodyCache<'_, 'tcx> {
+ ReadOnlyBodyCache::new(&self.cache, &self.body)
+ }
+
+ pub fn basic_blocks_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
+ self.cache.basic_blocks_mut(&mut self.body)
+ }
+
+ pub fn basic_blocks_and_local_decls_mut(
+ &mut self
+ ) -> (&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &mut LocalDecls<'tcx>) {
+ self.cache.basic_blocks_and_local_decls_mut(&mut self.body)
+ }
+}
+
+impl<'tcx> Index<BasicBlock> for BodyCache<'tcx> {
+ type Output = BasicBlockData<'tcx>;
+
+ fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> {
+ &self.body[index]
+ }
+}
+
+impl<'tcx> IndexMut<BasicBlock> for BodyCache<'tcx> {
+ fn index_mut(&mut self, index: BasicBlock) -> &mut Self::Output {
+ &mut self.basic_blocks_mut()[index]
+ }
+}
+
+impl<'tcx> Deref for BodyCache<'tcx> {
+ type Target = Body<'tcx>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.body
+ }
+}
+
+impl<'tcx> DerefMut for BodyCache<'tcx> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.body
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct ReadOnlyBodyCache<'a, 'tcx> {
+ cache: &'a Cache,
+ body: &'a Body<'tcx>,
+}
+
+impl ReadOnlyBodyCache<'a, 'tcx> {
+ fn new(cache: &'a Cache, body: &'a Body<'tcx>) -> Self {
+ assert!(
+ cache.predecessors.is_some(),
+ "Cannot construct ReadOnlyBodyCache without computed predecessors");
+ Self {
+ cache,
+ body,
}
}
- result
+ pub fn predecessors(&self) -> &IndexVec<BasicBlock, Vec<BasicBlock>> {
+ self.cache.predecessors.as_ref().unwrap()
+ }
+
+ pub fn predecessors_for(&self, bb: BasicBlock) -> &[BasicBlock] {
+ self.cache.unwrap_predecessors_for(bb)
+ }
+
+ pub fn predecessor_locations(&self, loc: Location) -> impl Iterator<Item = Location> + '_ {
+ self.cache.unwrap_predecessor_locations(loc, self.body)
+ }
+
+ pub fn body(&self) -> &'a Body<'tcx> {
+ self.body
+ }
+
+ pub fn basic_blocks(&self) -> &IndexVec<BasicBlock, BasicBlockData<'tcx>> {
+ &self.body.basic_blocks
+ }
+
+ pub fn dominators(&self) -> Dominators<BasicBlock> {
+ dominators(self)
+ }
+}
+
+impl graph::DirectedGraph for ReadOnlyBodyCache<'a, 'tcx> {
+ type Node = BasicBlock;
+}
+
+impl graph::GraphPredecessors<'graph> for ReadOnlyBodyCache<'a, 'tcx> {
+ type Item = BasicBlock;
+ type Iter = IntoIter<BasicBlock>;
+}
+
+impl graph::WithPredecessors for ReadOnlyBodyCache<'a, 'tcx> {
+ fn predecessors(
+ &self,
+ node: Self::Node,
+ ) -> <Self as GraphPredecessors<'_>>::Iter {
+ self.cache.unwrap_predecessors_for(node).to_vec().into_iter()
+ }
+}
+
+impl graph::WithNumNodes for ReadOnlyBodyCache<'a, 'tcx> {
+ fn num_nodes(&self) -> usize {
+ self.body.num_nodes()
+ }
+}
+
+impl graph::WithStartNode for ReadOnlyBodyCache<'a, 'tcx> {
+ fn start_node(&self) -> Self::Node {
+ self.body.start_node()
+ }
+}
+
+impl graph::WithSuccessors for ReadOnlyBodyCache<'a, 'tcx> {
+ fn successors(
+ &self,
+ node: Self::Node,
+ ) -> <Self as GraphSuccessors<'_>>::Iter {
+ self.body.successors(node)
+ }
+}
+
+impl<'a, 'b, 'tcx> graph::GraphSuccessors<'b> for ReadOnlyBodyCache<'a, 'tcx> {
+ type Item = BasicBlock;
+ type Iter = iter::Cloned<Successors<'b>>;
+}
+
+
+impl Deref for ReadOnlyBodyCache<'a, 'tcx> {
+ type Target = Body<'tcx>;
+
+ fn deref(&self) -> &Self::Target {
+ self.body
+ }
+}
+
+impl Index<BasicBlock> for ReadOnlyBodyCache<'a, 'tcx> {
+ type Output = BasicBlockData<'tcx>;
+
+ fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> {
+ &self.body[index]
+ }
}
CloneTypeFoldableAndLiftImpls! {
use syntax_pos::{Pos, Span};
use syntax::symbol::Symbol;
use hir::GeneratorKind;
-use std::{fmt, env};
+use std::{fmt, env, any::Any};
use rustc_error_codes::*;
pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
pub type ConstEvalResult<'tcx> = Result<&'tcx ty::Const<'tcx>, ErrorHandled>;
-#[derive(Clone, Debug)]
+#[derive(Debug)]
pub struct ConstEvalErr<'tcx> {
pub span: Span,
pub error: crate::mir::interpret::InterpError<'tcx>,
pub stacktrace: Vec<FrameInfo<'tcx>>,
}
-#[derive(Clone, Debug)]
+#[derive(Debug)]
pub struct FrameInfo<'tcx> {
/// This span is in the caller.
pub call_site: Span,
lint_root: Option<hir::HirId>,
) -> Result<DiagnosticBuilder<'tcx>, ErrorHandled> {
let must_error = match self.error {
+ InterpError::MachineStop(_) => bug!("CTFE does not stop"),
err_inval!(Layout(LayoutError::Unknown(_))) |
err_inval!(TooGeneric) =>
return Err(ErrorHandled::TooGeneric),
/// Thsese should always be constructed by calling `.into()` on
/// a `InterpError`. In `librustc_mir::interpret`, we have `throw_err_*`
/// macros for this.
-#[derive(Debug, Clone)]
+#[derive(Debug)]
pub struct InterpErrorInfo<'tcx> {
pub kind: InterpError<'tcx>,
backtrace: Option<Box<Backtrace>>,
/// Error information for when the program we executed turned out not to actually be a valid
/// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
/// where we work on generic code or execution does not have all information available.
-#[derive(Clone, HashStable)]
pub enum InvalidProgramInfo<'tcx> {
/// Resolution can fail if we are in a too generic context.
TooGeneric,
}
/// Error information for when the program caused Undefined Behavior.
-#[derive(Clone, HashStable)]
pub enum UndefinedBehaviorInfo {
/// Free-form case. Only for errors that are never caught!
Ub(String),
///
/// Currently, we also use this as fall-back error kind for errors that have not been
/// categorized yet.
-#[derive(Clone, HashStable)]
pub enum UnsupportedOpInfo<'tcx> {
/// Free-form case. Only for errors that are never caught!
Unsupported(String),
/// Error information for when the program exhausted the resources granted to it
/// by the interpreter.
-#[derive(Clone, HashStable)]
pub enum ResourceExhaustionInfo {
/// The stack grew too big.
StackFrameLimitReached,
}
}
-#[derive(Clone, HashStable)]
pub enum InterpError<'tcx> {
/// The program panicked.
Panic(PanicInfo<u64>),
/// The program did something the interpreter does not support (some of these *might* be UB
/// but the interpreter is not sure).
Unsupported(UnsupportedOpInfo<'tcx>),
- /// The program was invalid (ill-typed, not sufficiently monomorphized, ...).
+ /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
InvalidProgram(InvalidProgramInfo<'tcx>),
/// The program exhausted the interpreter's resources (stack/heap too big,
- /// execution takes too long, ..).
+ /// execution takes too long, ...).
ResourceExhaustion(ResourceExhaustionInfo),
- /// Not actually an interpreter error -- used to signal that execution has exited
- /// with the given status code. Used by Miri, but not by CTFE.
- Exit(i32),
+ /// Stop execution for a machine-controlled reason. This is never raised by
+ /// the core engine itself.
+ MachineStop(Box<dyn Any + Send>),
}
pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
write!(f, "{:?}", msg),
Panic(ref msg) =>
write!(f, "{:?}", msg),
- Exit(code) =>
- write!(f, "exited with status code {}", code),
+ MachineStop(_) =>
+ write!(f, "machine caused execution to stop"),
}
}
}
}
}
+impl<Tag> From<Pointer<Tag>> for ScalarMaybeUndef<Tag> {
+ #[inline(always)]
+ fn from(s: Pointer<Tag>) -> Self {
+ ScalarMaybeUndef::Scalar(s.into())
+ }
+}
+
impl<Tag: fmt::Debug, Id: fmt::Debug> fmt::Debug for ScalarMaybeUndef<Tag, Id> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
use polonius_engine::Atom;
use rustc_index::bit_set::BitMatrix;
use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::graph::dominators::{dominators, Dominators};
-use rustc_data_structures::graph::{self, GraphPredecessors, GraphSuccessors};
+use rustc_data_structures::graph::dominators::Dominators;
+use rustc_data_structures::graph::{self, GraphSuccessors};
use rustc_index::vec::{Idx, IndexVec};
use rustc_data_structures::sync::Lrc;
-use rustc_data_structures::sync::MappedReadGuard;
use rustc_macros::HashStable;
use rustc_serialize::{Encodable, Decodable};
use smallvec::SmallVec;
use std::borrow::Cow;
use std::fmt::{self, Debug, Display, Formatter, Write};
-use std::ops::{Index, IndexMut};
+use std::ops::Index;
use std::slice;
-use std::vec::IntoIter;
use std::{iter, mem, option, u32};
use syntax::ast::Name;
use syntax::symbol::Symbol;
use syntax_pos::{Span, DUMMY_SP};
pub use crate::mir::interpret::AssertMessage;
+pub use crate::mir::cache::{BodyCache, ReadOnlyBodyCache};
+pub use crate::read_only;
mod cache;
pub mod interpret;
/// and used for debuginfo. Indexed by a `SourceScope`.
pub source_scopes: IndexVec<SourceScope, SourceScopeData>,
- /// Crate-local information for each source scope, that can't (and
- /// needn't) be tracked across crates.
- pub source_scope_local_data: ClearCrossCrate<IndexVec<SourceScope, SourceScopeLocalData>>,
-
/// The yield type of the function, if it is a generator.
pub yield_ty: Option<Ty<'tcx>>,
/// Generator drop glue.
- pub generator_drop: Option<Box<Body<'tcx>>>,
+ pub generator_drop: Option<Box<BodyCache<'tcx>>>,
/// The layout of a generator. Produced by the state transformation.
pub generator_layout: Option<GeneratorLayout<'tcx>>,
/// A span representing this MIR, for error reporting.
pub span: Span,
-
- /// A cache for various calculations.
- cache: cache::Cache,
}
impl<'tcx> Body<'tcx> {
pub fn new(
basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
source_scopes: IndexVec<SourceScope, SourceScopeData>,
- source_scope_local_data: ClearCrossCrate<IndexVec<SourceScope, SourceScopeLocalData>>,
local_decls: LocalDecls<'tcx>,
user_type_annotations: CanonicalUserTypeAnnotations<'tcx>,
arg_count: usize,
phase: MirPhase::Build,
basic_blocks,
source_scopes,
- source_scope_local_data,
yield_ty: None,
generator_drop: None,
generator_layout: None,
spread_arg: None,
var_debug_info,
span,
- cache: cache::Cache::new(),
control_flow_destroyed,
}
}
&self.basic_blocks
}
- #[inline]
- pub fn basic_blocks_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
- self.cache.invalidate();
- &mut self.basic_blocks
- }
-
- #[inline]
- pub fn basic_blocks_and_local_decls_mut(
- &mut self,
- ) -> (&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &mut LocalDecls<'tcx>) {
- self.cache.invalidate();
- (&mut self.basic_blocks, &mut self.local_decls)
- }
-
- #[inline]
- pub fn predecessors(&self) -> MappedReadGuard<'_, IndexVec<BasicBlock, Vec<BasicBlock>>> {
- self.cache.predecessors(self)
- }
-
- #[inline]
- pub fn predecessors_for(&self, bb: BasicBlock) -> MappedReadGuard<'_, Vec<BasicBlock>> {
- MappedReadGuard::map(self.predecessors(), |p| &p[bb])
- }
-
- #[inline]
- pub fn predecessor_locations(&self, loc: Location) -> impl Iterator<Item = Location> + '_ {
- let if_zero_locations = if loc.statement_index == 0 {
- let predecessor_blocks = self.predecessors_for(loc.block);
- let num_predecessor_blocks = predecessor_blocks.len();
- Some(
- (0..num_predecessor_blocks)
- .map(move |i| predecessor_blocks[i])
- .map(move |bb| self.terminator_loc(bb)),
- )
- } else {
- None
- };
-
- let if_not_zero_locations = if loc.statement_index == 0 {
- None
- } else {
- Some(Location { block: loc.block, statement_index: loc.statement_index - 1 })
- };
-
- if_zero_locations.into_iter().flatten().chain(if_not_zero_locations)
- }
-
- #[inline]
- pub fn dominators(&self) -> Dominators<BasicBlock> {
- dominators(self)
- }
-
/// Returns `true` if a cycle exists in the control-flow graph that is reachable from the
/// `START_BLOCK`.
pub fn is_cfg_cyclic(&self) -> bool {
/// Changes a statement to a nop. This is both faster than deleting instructions and avoids
/// invalidating statement indices in `Location`s.
pub fn make_statement_nop(&mut self, location: Location) {
- let block = &mut self[location.block];
+ let block = &mut self.basic_blocks[location.block];
debug_assert!(location.statement_index < block.statements.len());
block.statements[location.statement_index].make_nop()
}
}
}
-impl<'tcx> IndexMut<BasicBlock> for Body<'tcx> {
- #[inline]
- fn index_mut(&mut self, index: BasicBlock) -> &mut BasicBlockData<'tcx> {
- &mut self.basic_blocks_mut()[index]
- }
-}
-
#[derive(Copy, Clone, Debug, HashStable, TypeFoldable)]
pub enum ClearCrossCrate<T> {
Clear,
}
impl<T> ClearCrossCrate<T> {
+ pub fn as_ref(&'a self) -> ClearCrossCrate<&'a T> {
+ match self {
+ ClearCrossCrate::Clear => ClearCrossCrate::Clear,
+ ClearCrossCrate::Set(v) => ClearCrossCrate::Set(v),
+ }
+ }
+
pub fn assert_crate_local(self) -> T {
match self {
ClearCrossCrate::Clear => bug!("unwrapping cross-crate data"),
pub struct SourceScopeData {
pub span: Span,
pub parent_scope: Option<SourceScope>,
+
+ /// Crate-local information for this source scope, that can't (and
+ /// needn't) be tracked across crates.
+ pub local_data: ClearCrossCrate<SourceScopeLocalData>,
}
#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
}
}
- AggregateKind::Closure(def_id, _) => ty::tls::with(|tcx| {
+ AggregateKind::Closure(def_id, substs) => ty::tls::with(|tcx| {
if let Some(hir_id) = tcx.hir().as_local_hir_id(def_id) {
let name = if tcx.sess.opts.debugging_opts.span_free_formats {
- format!("[closure@{:?}]", hir_id)
+ let substs = tcx.lift(&substs).unwrap();
+ format!(
+ "[closure@{}]",
+ tcx.def_path_str_with_substs(def_id, substs),
+ )
} else {
format!("[closure@{:?}]", tcx.hir().span(hir_id))
};
}
}
-impl<'tcx> graph::WithPredecessors for Body<'tcx> {
- fn predecessors(
- &self,
- node: Self::Node,
- ) -> <Self as GraphPredecessors<'_>>::Iter {
- self.predecessors_for(node).clone().into_iter()
- }
-}
-
impl<'tcx> graph::WithSuccessors for Body<'tcx> {
fn successors(
&self,
}
}
-impl<'a, 'b> graph::GraphPredecessors<'b> for Body<'a> {
- type Item = BasicBlock;
- type Iter = IntoIter<BasicBlock>;
-}
-
impl<'a, 'b> graph::GraphSuccessors<'b> for Body<'a> {
type Item = BasicBlock;
type Iter = iter::Cloned<Successors<'b>>;
}
/// Returns `true` if `other` is earlier in the control flow graph than `self`.
- pub fn is_predecessor_of<'tcx>(&self, other: Location, body: &Body<'tcx>) -> bool {
+ pub fn is_predecessor_of<'tcx>(
+ &self,
+ other: Location,
+ body: ReadOnlyBodyCache<'_, 'tcx>
+ ) -> bool {
// If we are in the same block as the other location and are an earlier statement
// then we are a predecessor of `other`.
if self.block == other.block && self.statement_index < other.statement_index {
}
// If we're in another block, then we want to check that block is a predecessor of `other`.
- let mut queue: Vec<BasicBlock> = body.predecessors_for(other.block).clone();
+ let mut queue: Vec<BasicBlock> = body.predecessors_for(other.block).to_vec();
let mut visited = FxHashSet::default();
while let Some(block) = queue.pop() {
// If we haven't visited this block before, then make sure we visit it's predecessors.
if visited.insert(block) {
- queue.append(&mut body.predecessors_for(block).clone());
+ queue.extend(body.predecessors_for(block).iter().cloned());
} else {
continue;
}
// variant argument) that does not require visiting, as in
// `is_cleanup` above.
+macro_rules! body_cache_type {
+ (mut $a:lifetime, $tcx:lifetime) => {
+ &mut BodyCache<$tcx>
+ };
+ ($a:lifetime, $tcx:lifetime) => {
+ ReadOnlyBodyCache<$a, $tcx>
+ };
+}
+
macro_rules! make_mir_visitor {
($visitor_trait_name:ident, $($mutability:ident)?) => {
pub trait $visitor_trait_name<'tcx> {
// Override these, and call `self.super_xxx` to revert back to the
// default behavior.
- fn visit_body(&mut self, body: & $($mutability)? Body<'tcx>) {
+ fn visit_body(
+ &mut self,
+ body: body_cache_type!($($mutability)? '_, 'tcx)
+ ) {
self.super_body(body);
}
// The `super_xxx` methods comprise the default behavior and are
// not meant to be overridden.
- fn super_body(&mut self,
- body: & $($mutability)? Body<'tcx>) {
+ fn super_body(
+ &mut self,
+ $($mutability)? body: body_cache_type!($($mutability)? '_, 'tcx)
+ ) {
+ let span = body.span;
if let Some(yield_ty) = &$($mutability)? body.yield_ty {
self.visit_ty(yield_ty, TyContext::YieldTy(SourceInfo {
- span: body.span,
+ span,
scope: OUTERMOST_SOURCE_SCOPE,
}));
}
self.visit_basic_block_data(bb, data);
}
+ let body: & $($mutability)? Body<'_> = & $($mutability)? body;
for scope in &$($mutability)? body.source_scopes {
self.visit_source_scope_data(scope);
}
let SourceScopeData {
span,
parent_scope,
+ local_data: _,
} = scope_data;
self.visit_span(span);
// Convenience methods
- fn visit_location(&mut self, body: & $($mutability)? Body<'tcx>, location: Location) {
+ fn visit_location(
+ &mut self,
+ body: body_cache_type!($($mutability)? '_, 'tcx),
+ location: Location
+ ) {
let basic_block = & $($mutability)? body[location.block];
if basic_block.statements.len() == location.statement_index {
if let Some(ref $($mutability)? terminator) = basic_block.terminator {
/// Fetch the MIR for a given `DefId` right after it's built - this includes
/// unreachable code.
- query mir_built(_: DefId) -> &'tcx Steal<mir::Body<'tcx>> {}
+ query mir_built(_: DefId) -> &'tcx Steal<mir::BodyCache<'tcx>> {}
/// Fetch the MIR for a given `DefId` up till the point where it is
/// ready for const evaluation.
///
/// See the README for the `mir` module for details.
- query mir_const(_: DefId) -> &'tcx Steal<mir::Body<'tcx>> {
+ query mir_const(_: DefId) -> &'tcx Steal<mir::BodyCache<'tcx>> {
no_hash
}
query mir_validated(_: DefId) ->
(
- &'tcx Steal<mir::Body<'tcx>>,
- &'tcx Steal<IndexVec<mir::Promoted, mir::Body<'tcx>>>
+ &'tcx Steal<mir::BodyCache<'tcx>>,
+ &'tcx Steal<IndexVec<mir::Promoted, mir::BodyCache<'tcx>>>
) {
no_hash
}
/// MIR after our optimization passes have run. This is MIR that is ready
/// for codegen. This is also the only query that can fetch non-local MIR, at present.
- query optimized_mir(key: DefId) -> &'tcx mir::Body<'tcx> {
+ query optimized_mir(key: DefId) -> &'tcx mir::BodyCache<'tcx> {
cache_on_disk_if { key.is_local() }
load_cached(tcx, id) {
- let mir: Option<crate::mir::Body<'tcx>> = tcx.queries.on_disk_cache
- .try_load_query_result(tcx, id);
- mir.map(|x| &*tcx.arena.alloc(x))
+ let mir: Option<crate::mir::BodyCache<'tcx>>
+ = tcx.queries.on_disk_cache.try_load_query_result(tcx, id);
+ mir.map(|x| {
+ let cache = tcx.arena.alloc(x);
+ cache.ensure_predecessors();
+ &*cache
+ })
}
}
- query promoted_mir(key: DefId) -> &'tcx IndexVec<mir::Promoted, mir::Body<'tcx>> {
+ query promoted_mir(key: DefId) -> &'tcx IndexVec<mir::Promoted, mir::BodyCache<'tcx>> {
cache_on_disk_if { key.is_local() }
load_cached(tcx, id) {
let promoted: Option<
rustc_index::vec::IndexVec<
crate::mir::Promoted,
- crate::mir::Body<'tcx>
+ crate::mir::BodyCache<'tcx>
>> = tcx.queries.on_disk_cache.try_load_query_result(tcx, id);
- promoted.map(|p| &*tcx.arena.alloc(p))
+ promoted.map(|p| {
+ let cache = tcx.arena.alloc(p);
+ for body in cache.iter_mut() {
+ body.ensure_predecessors();
+ }
+ &*cache
+ })
}
}
}
/// in the case of closures, this will be redirected to the enclosing function.
query region_scope_tree(_: DefId) -> &'tcx region::ScopeTree {}
- query mir_shims(key: ty::InstanceDef<'tcx>) -> &'tcx mir::Body<'tcx> {
+ query mir_shims(key: ty::InstanceDef<'tcx>) -> &'tcx mir::BodyCache<'tcx> {
no_force
desc { |tcx| "generating MIR shim for `{}`", tcx.def_path_str(key.def_id()) }
}
Thread,
}
+impl fmt::Display for Sanitizer {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ Sanitizer::Address => "address".fmt(f),
+ Sanitizer::Leak => "leak".fmt(f),
+ Sanitizer::Memory => "memory".fmt(f),
+ Sanitizer::Thread => "thread".fmt(f),
+ }
+ }
+}
+
impl FromStr for Sanitizer {
type Err = ();
fn from_str(s: &str) -> Result<Sanitizer, ()> {
"enable queries of the dependency graph for regression testing"),
no_analysis: bool = (false, parse_bool, [UNTRACKED],
"parse and expand the source, but run no analysis"),
- extra_plugins: Vec<String> = (Vec::new(), parse_list, [TRACKED],
- "load extra plugins"),
unstable_options: bool = (false, parse_bool, [UNTRACKED],
"adds unstable command line options to rustc interface"),
force_overflow_checks: Option<bool> = (None, parse_opt_bool, [TRACKED],
}
}
}
+ if let Some(s) = &sess.opts.debugging_opts.sanitizer {
+ let symbol = Symbol::intern(&s.to_string());
+ ret.insert((sym::sanitize, Some(symbol)));
+ }
if sess.opts.debug_assertions {
ret.insert((Symbol::intern("debug_assertions"), None));
}
/// (sub)diagnostics that have been set once, but should not be set again,
/// in order to avoid redundantly verbose output (Issue #24690, #44953).
pub one_time_diagnostics: Lock<FxHashSet<(DiagnosticMessageId, Option<Span>, String)>>,
- pub plugin_llvm_passes: OneThread<RefCell<Vec<String>>>,
pub crate_types: Once<Vec<config::CrateType>>,
/// The `crate_disambiguator` is constructed out of all the `-C metadata`
/// arguments passed to the compiler. Its value together with the crate-name
local_crate_source_file,
working_dir,
one_time_diagnostics: Default::default(),
- plugin_llvm_passes: OneThread::new(RefCell::new(Vec::new())),
crate_types: Once::new(),
crate_disambiguator: Once::new(),
features: Once::new(),
) {
command.evaluate(self.tcx, trait_ref, &flags[..])
} else {
- OnUnimplementedNote::empty()
+ OnUnimplementedNote::default()
}
}
fallback_has_occurred: bool,
points_at_arg: bool,
) {
+ let tcx = self.tcx;
let span = obligation.cause.span;
let mut err = match *error {
message,
label,
note,
+ enclosing_scope,
} = self.on_unimplemented_note(trait_ref, obligation);
let have_alt_message = message.is_some() || label.is_some();
let is_try = self.tcx.sess.source_map().span_to_snippet(span)
// If it has a custom `#[rustc_on_unimplemented]` note, let's display it
err.note(s.as_str());
}
+ if let Some(ref s) = enclosing_scope {
+ let enclosing_scope_span = tcx.def_span(
+ tcx.hir()
+ .opt_local_def_id(obligation.cause.body_id)
+ .unwrap_or_else(|| {
+ tcx.hir().body_owner_def_id(hir::BodyId {
+ hir_id: obligation.cause.body_id,
+ })
+ }),
+ );
+
+ err.span_label(enclosing_scope_span, s.as_str());
+ }
self.suggest_borrow_on_unsized_slice(&obligation.cause.code, &mut err);
self.suggest_fn_call(&obligation, &mut err, &trait_ref, points_at_arg);
pub message: Option<OnUnimplementedFormatString>,
pub label: Option<OnUnimplementedFormatString>,
pub note: Option<OnUnimplementedFormatString>,
+ pub enclosing_scope: Option<OnUnimplementedFormatString>,
}
+#[derive(Default)]
pub struct OnUnimplementedNote {
pub message: Option<String>,
pub label: Option<String>,
pub note: Option<String>,
-}
-
-impl OnUnimplementedNote {
- pub fn empty() -> Self {
- OnUnimplementedNote { message: None, label: None, note: None }
- }
+ pub enclosing_scope: Option<String>,
}
fn parse_error(
let mut message = None;
let mut label = None;
let mut note = None;
+ let mut enclosing_scope = None;
let mut subcommands = vec![];
+
+ let parse_value = |value_str| {
+ OnUnimplementedFormatString::try_parse(tcx, trait_def_id, value_str, span)
+ .map(Some)
+ };
+
for item in item_iter {
if item.check_name(sym::message) && message.is_none() {
if let Some(message_) = item.value_str() {
- message = Some(OnUnimplementedFormatString::try_parse(
- tcx, trait_def_id, message_, span)?);
+ message = parse_value(message_)?;
continue;
}
} else if item.check_name(sym::label) && label.is_none() {
if let Some(label_) = item.value_str() {
- label = Some(OnUnimplementedFormatString::try_parse(
- tcx, trait_def_id, label_, span)?);
+ label = parse_value(label_)?;
continue;
}
} else if item.check_name(sym::note) && note.is_none() {
if let Some(note_) = item.value_str() {
- note = Some(OnUnimplementedFormatString::try_parse(
- tcx, trait_def_id, note_, span)?);
+ note = parse_value(note_)?;
+ continue;
+ }
+ } else if item.check_name(sym::enclosing_scope) && enclosing_scope.is_none() {
+ if let Some(enclosing_scope_) = item.value_str() {
+ enclosing_scope = parse_value(enclosing_scope_)?;
continue;
}
} else if item.check_name(sym::on) && is_root &&
if errored {
Err(ErrorReported)
} else {
- Ok(OnUnimplementedDirective { condition, message, label, subcommands, note })
+ Ok(OnUnimplementedDirective {
+ condition,
+ subcommands,
+ message,
+ label,
+ note,
+ enclosing_scope
+ })
}
}
label: Some(OnUnimplementedFormatString::try_parse(
tcx, trait_def_id, value, attr.span)?),
note: None,
+ enclosing_scope: None,
}))
} else {
return Err(ErrorReported);
let mut message = None;
let mut label = None;
let mut note = None;
+ let mut enclosing_scope = None;
info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
for command in self.subcommands.iter().chain(Some(self)).rev() {
if let Some(ref note_) = command.note {
note = Some(note_.clone());
}
+
+ if let Some(ref enclosing_scope_) = command.enclosing_scope {
+ enclosing_scope = Some(enclosing_scope_.clone());
+ }
}
let options: FxHashMap<Symbol, String> = options.into_iter()
label: label.map(|l| l.format(tcx, trait_ref, &options)),
message: message.map(|m| m.format(tcx, trait_ref, &options)),
note: note.map(|n| n.format(tcx, trait_ref, &options)),
+ enclosing_scope: enclosing_scope.map(|e_s| e_s.format(tcx, trait_ref, &options)),
}
}
}
use crate::middle::lang_items;
use crate::middle::resolve_lifetime::{self, ObjectLifetimeDefault};
use crate::middle::stability;
-use crate::mir::{Body, Field, interpret, Local, Place, PlaceElem, ProjectionKind, Promoted};
+use crate::mir::{BodyCache, Field, interpret, Local, Place, PlaceElem, ProjectionKind, Promoted};
use crate::mir::interpret::{ConstValue, Allocation, Scalar};
use crate::ty::subst::{GenericArg, InternalSubsts, SubstsRef, Subst};
use crate::ty::ReprOptions;
&self.hir_map
}
- pub fn alloc_steal_mir(self, mir: Body<'tcx>) -> &'tcx Steal<Body<'tcx>> {
+ pub fn alloc_steal_mir(self, mir: BodyCache<'tcx>) -> &'tcx Steal<BodyCache<'tcx>> {
self.arena.alloc(Steal::new(mir))
}
- pub fn alloc_steal_promoted(self, promoted: IndexVec<Promoted, Body<'tcx>>) ->
- &'tcx Steal<IndexVec<Promoted, Body<'tcx>>> {
+ pub fn alloc_steal_promoted(self, promoted: IndexVec<Promoted, BodyCache<'tcx>>) ->
+ &'tcx Steal<IndexVec<Promoted, BodyCache<'tcx>>> {
self.arena.alloc(Steal::new(promoted))
}
- pub fn intern_promoted(self, promoted: IndexVec<Promoted, Body<'tcx>>) ->
- &'tcx IndexVec<Promoted, Body<'tcx>> {
+ pub fn intern_promoted(self, promoted: IndexVec<Promoted, BodyCache<'tcx>>) ->
+ &'tcx IndexVec<Promoted, BodyCache<'tcx>> {
self.arena.alloc(promoted)
}
use crate::middle::cstore::CrateStoreDyn;
use crate::middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem};
use crate::middle::resolve_lifetime::ObjectLifetimeDefault;
-use crate::mir::Body;
+use crate::mir::ReadOnlyBodyCache;
use crate::mir::interpret::{GlobalId, ErrorHandled};
use crate::mir::GeneratorLayout;
use crate::session::CrateDisambiguator;
}
/// Returns the possibly-auto-generated MIR of a `(DefId, Subst)` pair.
- pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> &'tcx Body<'tcx> {
+ pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> ReadOnlyBodyCache<'tcx, 'tcx> {
match instance {
ty::InstanceDef::Item(did) => {
- self.optimized_mir(did)
+ self.optimized_mir(did).unwrap_read_only()
}
ty::InstanceDef::VtableShim(..) |
ty::InstanceDef::ReifyShim(..) |
ty::InstanceDef::ClosureOnceShim { .. } |
ty::InstanceDef::DropGlue(..) |
ty::InstanceDef::CloneShim(..) => {
- self.mir_shims(instance)
+ self.mir_shims(instance).unwrap_read_only()
}
}
}
// FIXME(eddyb) should use `def_span`.
if let Some(hir_id) = self.tcx().hir().as_local_hir_id(did) {
if self.tcx().sess.opts.debugging_opts.span_free_formats {
- p!(write("@{:?}", hir_id));
+ p!(write("@"), print_def_path(did, substs));
} else {
p!(write("@{:?}", self.tcx().hir().span(hir_id)));
}
//! hand, though we've recently added some macros and proc-macros to help with the tedium.
use crate::hir::def::Namespace;
+use crate::hir::def_id::CRATE_DEF_INDEX;
use crate::mir::ProjectionKind;
use crate::mir::interpret;
use crate::ty::{self, Lift, Ty, TyCtxt, InferConst};
match *self {
ty::BrAnon(n) => write!(f, "BrAnon({:?})", n),
ty::BrNamed(did, name) => {
- write!(f, "BrNamed({:?}:{:?}, {})",
- did.krate, did.index, name)
+ if did.index == CRATE_DEF_INDEX {
+ write!(f, "BrNamed({})", name)
+ } else {
+ write!(f, "BrNamed({:?}, {})", did, name)
+ }
}
ty::BrEnv => write!(f, "BrEnv"),
}
add_sanitizer_passes(config, &mut extra_passes);
- for pass_name in &cgcx.plugin_passes {
- if let Some(pass) = find_pass(pass_name) {
- extra_passes.push(pass);
- } else {
- diag_handler.err(&format!("a plugin asked for LLVM pass \
- `{}` but LLVM does not \
- recognize it", pass_name));
- }
-
- if pass_name == "name-anon-globals" {
- have_name_anon_globals_pass = true;
- }
- }
-
// Some options cause LLVM bitcode to be emitted, which uses ThinLTOBuffers, so we need
// to make sure we run LLVM's NameAnonGlobals pass when emitting bitcode; otherwise
// we'll get errors in LLVM.
("rclass", Some(sym::arm_target_feature)),
("dsp", Some(sym::arm_target_feature)),
("neon", Some(sym::arm_target_feature)),
+ ("crc", Some(sym::arm_target_feature)),
+ ("crypto", Some(sym::arm_target_feature)),
("v5te", Some(sym::arm_target_feature)),
("v6", Some(sym::arm_target_feature)),
("v6k", Some(sym::arm_target_feature)),
pub total_cgus: usize,
// Handler to use for diagnostics produced during codegen.
pub diag_emitter: SharedEmitter,
- // LLVM passes added by plugins.
- pub plugin_passes: Vec<String>,
// LLVM optimizations for which we want to print remarks.
pub remark: Passes,
// Worker thread number
time_passes: sess.time_extended(),
prof: sess.prof.clone(),
exported_symbols,
- plugin_passes: sess.plugin_llvm_passes.borrow().clone(),
remark: sess.opts.cg.remark.clone(),
worker: 0,
incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()),
let lldecl = cx.get_fn(instance);
let mir = cx.tcx().instance_mir(instance.def);
- mir::codegen_mir::<Bx>(cx, lldecl, &mir, instance, sig);
+ mir::codegen_mir::<Bx>(cx, lldecl, mir, instance, sig);
}
/// Creates the `main` function which will initialize the rust runtime and call
// FIXME(eddyb): We should figure out how to use llvm.dbg.value instead
// of putting everything in allocas just so we can use llvm.dbg.declare.
if fx.cx.sess().opts.debuginfo == DebugInfo::Full {
- if mir.local_kind(local) == mir::LocalKind::Arg {
+ if fx.mir.local_kind(local) == mir::LocalKind::Arg {
analyzer.not_ssa(local);
continue;
}
fn new(fx: &'mir FunctionCx<'a, 'tcx, Bx>) -> Self {
let invalid_location =
mir::BasicBlock::new(fx.mir.basic_blocks().len()).start_location();
+ let dominators = fx.mir.dominators();
let mut analyzer = LocalAnalyzer {
fx,
- dominators: fx.mir.dominators(),
+ dominators,
non_ssa_locals: BitSet::new_empty(fx.mir.local_decls.len()),
first_assignment: IndexVec::from_elem(invalid_location, &fx.mir.local_decls)
};
};
if is_consume {
let base_ty =
- mir::Place::ty_from(place_ref.base, proj_base, self.fx.mir, cx.tcx());
+ mir::Place::ty_from(place_ref.base, proj_base, &*self.fx.mir, cx.tcx());
let base_ty = self.fx.monomorphize(&base_ty);
// ZSTs don't require any actual memory access.
// a loop.
fn maybe_sideeffect<'b, 'tcx2: 'b, Bx: BuilderMethods<'b, 'tcx2>>(
&self,
- mir: &'b mir::Body<'tcx>,
+ mir: mir::ReadOnlyBodyCache<'b, 'tcx>,
bx: &mut Bx,
targets: &[mir::BasicBlock],
) {
target: mir::BasicBlock,
unwind: Option<mir::BasicBlock>,
) {
- let ty = location.ty(self.mir, bx.tcx()).ty;
+ let ty = location.ty(&*self.mir, bx.tcx()).ty;
let ty = self.monomorphize(&ty);
let drop_fn = Instance::resolve_drop_in_place(bx.tcx(), ty);
let extra_args = &args[sig.inputs().len()..];
let extra_args = extra_args.iter().map(|op_arg| {
- let op_ty = op_arg.ty(self.mir, bx.tcx());
+ let op_ty = op_arg.ty(&*self.mir, bx.tcx());
self.monomorphize(&op_ty)
}).collect::<Vec<_>>();
_ => FnAbi::new(&bx, sig, &extra_args)
};
- // This should never be reachable at runtime:
- // We should only emit a call to this intrinsic in #[cfg(miri)] mode,
- // which means that we will never actually use the generate object files
- // (we will just be interpreting the MIR)
- //
- // Note that we still need to be able to codegen *something* for this intrisnic:
- // Miri currently uses Xargo to build a special libstd. As a side effect,
- // we generate normal object files for libstd - while these are never used,
- // we still need to be able to build them.
+ // For normal codegen, this Miri-specific intrinsic is just a NOP.
if intrinsic == Some("miri_start_panic") {
- bx.abort();
- bx.unreachable();
+ let target = destination.as_ref().unwrap().1;
+ helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
+ helper.funclet_br(self, &mut bx, target);
return;
}
// a NOP
let target = destination.as_ref().unwrap().1;
helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
- helper.funclet_br(self, &mut bx, target);
+ helper.funclet_br(self, &mut bx, target)
}
return;
}
bb: mir::BasicBlock,
) {
let mut bx = self.build_block(bb);
- let data = &self.mir[bb];
+ let mir = self.mir;
+ let data = &mir[bb];
debug!("codegen_block({:?}={:?})", bb, data);
fn landing_pad_uncached(
&mut self,
- target_bb: Bx::BasicBlock
+ target_bb: Bx::BasicBlock,
) -> Bx::BasicBlock {
if base::wants_msvc_seh(self.cx.sess()) {
span_bug!(self.mir.span, "landing pad was not inserted?")
use rustc::ty::{self, Ty, TypeFoldable, Instance};
use rustc::ty::layout::{TyLayout, HasTyCtxt, FnAbiExt};
-use rustc::mir::{self, Body};
+use rustc::mir::{self, Body, ReadOnlyBodyCache};
use rustc_target::abi::call::{FnAbi, PassMode};
use crate::base;
use crate::traits::*;
pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
instance: Instance<'tcx>,
- mir: &'a mir::Body<'tcx>,
+ mir: mir::ReadOnlyBodyCache<'a, 'tcx>,
debug_context: Option<FunctionDebugContext<Bx::DIScope>>,
pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
cx: &'a Bx::CodegenCx,
llfn: Bx::Function,
- mir: &'a Body<'tcx>,
+ mir: ReadOnlyBodyCache<'a, 'tcx>,
instance: Instance<'tcx>,
sig: ty::FnSig<'tcx>,
) {
debug!("fn_abi: {:?}", fn_abi);
let debug_context =
- cx.create_function_debug_context(instance, sig, llfn, mir);
+ cx.create_function_debug_context(instance, sig, llfn, &mir);
let mut bx = Bx::new_block(cx, llfn, "start");
}
}).collect();
- let (landing_pads, funclets) = create_funclets(mir, &mut bx, &cleanup_kinds, &block_bxs);
-
+ let (landing_pads, funclets) = create_funclets(&mir, &mut bx, &cleanup_kinds, &block_bxs);
+ let mir_body: &Body<'_> = mir.body();
let mut fx = FunctionCx {
instance,
mir,
funclets,
locals: IndexVec::new(),
debug_context,
- per_local_var_debug_info: debuginfo::per_local_var_debug_info(cx.tcx(), mir),
+ per_local_var_debug_info: debuginfo::per_local_var_debug_info(cx.tcx(), mir_body),
};
let memory_locals = analyze::non_ssa_locals(&fx);
let args = arg_local_refs(&mut bx, &fx, &memory_locals);
let mut allocate_local = |local| {
- let decl = &mir.local_decls[local];
+ let decl = &mir_body.local_decls[local];
let layout = bx.layout_of(fx.monomorphize(&decl.ty));
assert!(!layout.ty.has_erasable_regions());
let retptr = allocate_local(mir::RETURN_PLACE);
iter::once(retptr)
.chain(args.into_iter())
- .chain(mir.vars_and_temps_iter().map(allocate_local))
+ .chain(mir_body.vars_and_temps_iter().map(allocate_local))
.collect()
};
debug_context.source_locations_enabled = true;
}
- let rpo = traversal::reverse_postorder(&mir);
- let mut visited = BitSet::new_empty(mir.basic_blocks().len());
+ let rpo = traversal::reverse_postorder(&mir_body);
+ let mut visited = BitSet::new_empty(mir_body.basic_blocks().len());
// Codegen the body of each block using reverse postorder
for (bb, _) in rpo {
// Remove blocks that haven't been visited, or have no
// predecessors.
- for bb in mir.basic_blocks().indices() {
+ for bb in mir_body.basic_blocks().indices() {
// Unreachable block
if !visited.contains(bb.index()) {
debug!("codegen_mir: block {:?} was not visited", bb);
}
}
-fn create_funclets<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
- mir: &'a Body<'tcx>,
+fn create_funclets<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+ mir: &'b Body<'tcx>,
bx: &mut Bx,
cleanup_kinds: &IndexVec<mir::BasicBlock, CleanupKind>,
block_bxs: &IndexVec<mir::BasicBlock, Bx::BasicBlock>,
pub fn monomorphized_place_ty(&self, place_ref: &mir::PlaceRef<'_, 'tcx>) -> Ty<'tcx> {
let tcx = self.cx.tcx();
- let place_ty = mir::Place::ty_from(place_ref.base, place_ref.projection, self.mir, tcx);
+ let place_ty = mir::Place::ty_from(
+ place_ref.base,
+ place_ref.projection,
+ &*self.mir,
+ tcx,
+ );
self.monomorphize(&place_ty.ty)
}
}
mir::Rvalue::Aggregate(..) => {
// According to `rvalue_creates_operand`, only ZST
// aggregate rvalues are allowed to be operands.
- let ty = rvalue.ty(self.mir, self.cx.tcx());
+ let ty = rvalue.ty(&*self.mir, self.cx.tcx());
let operand = OperandRef::new_zst(
&mut bx,
self.cx.layout_of(self.monomorphize(&ty)),
true,
mir::Rvalue::Repeat(..) |
mir::Rvalue::Aggregate(..) => {
- let ty = rvalue.ty(self.mir, self.cx.tcx());
+ let ty = rvalue.ty(&*self.mir, self.cx.tcx());
let ty = self.monomorphize(&ty);
self.cx.spanned_layout_of(ty, span).is_zst()
}
use rustc_index::vec::{Idx, IndexVec};
use super::iterate::reverse_post_order;
use super::ControlFlowGraph;
+use std::borrow::BorrowMut;
#[cfg(test)]
mod tests;
-pub fn dominators<G: ControlFlowGraph>(graph: &G) -> Dominators<G::Node> {
+pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
let start_node = graph.start_node();
- let rpo = reverse_post_order(graph, start_node);
+ let rpo = reverse_post_order(&graph, start_node);
dominators_given_rpo(graph, &rpo)
}
-fn dominators_given_rpo<G: ControlFlowGraph>(
- graph: &G,
+fn dominators_given_rpo<G: ControlFlowGraph + BorrowMut<G>>(
+ mut graph: G,
rpo: &[G::Node],
) -> Dominators<G::Node> {
- let start_node = graph.start_node();
+ let start_node = graph.borrow().start_node();
assert_eq!(rpo[0], start_node);
// compute the post order index (rank) for each node
let mut post_order_rank: IndexVec<G::Node, usize> =
- (0..graph.num_nodes()).map(|_| 0).collect();
+ (0..graph.borrow().num_nodes()).map(|_| 0).collect();
for (index, node) in rpo.iter().rev().cloned().enumerate() {
post_order_rank[node] = index;
}
let mut immediate_dominators: IndexVec<G::Node, Option<G::Node>> =
- (0..graph.num_nodes()).map(|_| None).collect();
+ (0..graph.borrow().num_nodes()).map(|_| None).collect();
immediate_dominators[start_node] = Some(start_node);
let mut changed = true;
for &node in &rpo[1..] {
let mut new_idom = None;
- for pred in graph.predecessors(node) {
+ for pred in graph.borrow_mut().predecessors(node) {
if immediate_dominators[pred].is_some() {
// (*) dominators for `pred` have been calculated
new_idom = Some(if let Some(new_idom) = new_idom {
/// Allows the use of `if` and `match` in constants.
(active, const_if_match, "1.41.0", Some(49146), None),
+ /// Allows the use of `#[cfg(sanitize = "option")]`; set when -Zsanitizer is used.
+ (active, cfg_sanitize, "1.41.0", Some(39699), None),
+
// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
(sym::target_thread_local, sym::cfg_target_thread_local, cfg_fn!(cfg_target_thread_local)),
(sym::target_has_atomic, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
(sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
+ (sym::sanitize, sym::cfg_sanitize, cfg_fn!(cfg_sanitize)),
];
/// Find a gated cfg determined by the `pred`icate which is given the cfg's name.
)
),
(
- sym::plugin, CrateLevel, template!(List: "name|name(args)"),
+ sym::plugin, CrateLevel, template!(List: "name"),
Gated(
Stability::Deprecated(
"https://github.com/rust-lang/rust/pull/64675",
use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str};
use rustc_passes::{self, ast_validation, hir_stats, layout_test};
use rustc_plugin_impl as plugin;
-use rustc_plugin_impl::registry::Registry;
use rustc_privacy;
use rustc_resolve::{Resolver, ResolverArenas};
use rustc_traits;
(&mut Resolver<'_>) -> (Result<ast::Crate>, ResolverOutputs)
);
-/// Runs the "early phases" of the compiler: initial `cfg` processing,
-/// loading compiler plugins (including those from `addl_plugins`),
+/// Runs the "early phases" of the compiler: initial `cfg` processing, loading compiler plugins,
/// syntax expansion, secondary `cfg` expansion, synthesis of a test
/// harness if one is to be provided, injection of a dependency on the
/// standard library and prelude, and name resolution.
middle::recursion_limit::update_limits(sess, &krate);
});
- let registrars = time(sess, "plugin loading", || {
- plugin::load::load_plugins(
- sess,
- metadata_loader,
- &krate,
- Some(sess.opts.debugging_opts.extra_plugins.clone()),
- )
- });
-
let mut lint_store = rustc_lint::new_lint_store(
sess.opts.debugging_opts.no_interleave_lints,
sess.unstable_options(),
);
+ register_lints(&sess, &mut lint_store);
- (register_lints)(&sess, &mut lint_store);
-
- let mut registry = Registry::new(sess, &mut lint_store, krate.span);
-
+ let registrars = time(sess, "plugin loading", || {
+ plugin::load::load_plugins(sess, metadata_loader, &krate)
+ });
time(sess, "plugin registration", || {
+ let mut registry = plugin::Registry { lint_store: &mut lint_store };
for registrar in registrars {
- registry.args_hidden = Some(registrar.args);
- (registrar.fun)(&mut registry);
+ registrar(&mut registry);
}
});
- *sess.plugin_llvm_passes.borrow_mut() = registry.llvm_passes;
-
Ok((krate, Lrc::new(lint_store)))
}
opts.debugging_opts.continue_parse_after_error = true;
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
- opts = reference.clone();
- opts.debugging_opts.extra_plugins = vec![String::from("plugin1"), String::from("plugin2")];
- assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
-
opts = reference.clone();
opts.debugging_opts.force_overflow_checks = Some(true);
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
use std::fmt::Write;
use rustc::hir::def::{Res, DefKind};
-use rustc::hir::def_id::{DefId, LOCAL_CRATE};
+use rustc::hir::def_id::DefId;
use rustc::ty::{self, Ty, TyCtxt, layout::VariantIdx};
use rustc::{lint, util};
use rustc::lint::FutureIncompatibleInfo;
}
}
-declare_lint! {
- PLUGIN_AS_LIBRARY,
- Warn,
- "compiler plugin used as ordinary library in non-plugin crate"
-}
-
-declare_lint_pass!(PluginAsLibrary => [PLUGIN_AS_LIBRARY]);
-
-impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PluginAsLibrary {
- fn check_item(&mut self, cx: &LateContext<'_, '_>, it: &hir::Item) {
- if cx.tcx.plugin_registrar_fn(LOCAL_CRATE).is_some() {
- // We're compiling a plugin; it's fine to link other plugins.
- return;
- }
-
- match it.kind {
- hir::ItemKind::ExternCrate(..) => (),
- _ => return,
- };
-
- let def_id = cx.tcx.hir().local_def_id(it.hir_id);
- let prfn = match cx.tcx.extern_mod_stmt_cnum(def_id) {
- Some(cnum) => cx.tcx.plugin_registrar_fn(cnum),
- None => {
- // Probably means we aren't linking the crate for some reason.
- //
- // Not sure if / when this could happen.
- return;
- }
- };
-
- if prfn.is_some() {
- cx.span_lint(PLUGIN_AS_LIBRARY,
- it.span,
- "compiler plugin used as an ordinary library");
- }
- }
-}
-
declare_lint! {
NO_MANGLE_CONST_ITEMS,
Deny,
MISSING_DEBUG_IMPLEMENTATIONS,
ANONYMOUS_PARAMETERS,
UNUSED_DOC_COMMENTS,
- PLUGIN_AS_LIBRARY,
NO_MANGLE_CONST_ITEMS,
NO_MANGLE_GENERIC_ITEMS,
MUTABLE_TRANSMUTES,
impl EarlyLintPass for KeywordIdents {
fn check_mac_def(&mut self, cx: &EarlyContext<'_>, mac_def: &ast::MacroDef, _id: ast::NodeId) {
- self.check_tokens(cx, mac_def.stream());
+ self.check_tokens(cx, mac_def.body.inner_tokens());
}
fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::Mac) {
- self.check_tokens(cx, mac.tts.clone().into());
+ self.check_tokens(cx, mac.args.inner_tokens());
}
fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: ast::Ident) {
self.check_ident_token(cx, UnderMacro(false), ident);
// Depends on types used in type definitions
MissingCopyImplementations: MissingCopyImplementations,
- PluginAsLibrary: PluginAsLibrary,
-
// Depends on referenced function signatures in expressions
MutableTransmutes: MutableTransmutes,
"converted into hard error, see https://github.com/rust-lang/rust/issues/35896");
store.register_removed("nested_impl_trait",
"converted into hard error, see https://github.com/rust-lang/rust/issues/59014");
+ store.register_removed("plugin_as_library", "plugins have been deprecated and retired");
}
fn register_internals(store: &mut lint::LintStore) {
use rustc_data_structures::svh::Svh;
use rustc::dep_graph::{self, DepNodeIndex};
use rustc::middle::lang_items;
-use rustc::mir::{self, interpret};
+use rustc::mir::{self, BodyCache, interpret, Promoted};
use rustc::mir::interpret::{AllocDecodingSession, AllocDecodingState};
use rustc::session::Session;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::codec::TyDecoder;
-use rustc::mir::{Body, Promoted};
use rustc::util::common::record_time;
use rustc::util::captures::Captures;
self.root.per_def.mir.get(self, id).is_some()
}
- fn get_optimized_mir(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> Body<'tcx> {
- self.root.per_def.mir.get(self, id)
+ fn get_optimized_mir(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> BodyCache<'tcx> {
+ let mut cache = self.root.per_def.mir.get(self, id)
.filter(|_| !self.is_proc_macro(id))
.unwrap_or_else(|| {
bug!("get_optimized_mir: missing MIR for `{:?}`", self.local_def_id(id))
})
- .decode((self, tcx))
+ .decode((self, tcx));
+ cache.ensure_predecessors();
+ cache
}
fn get_promoted_mir(
&self,
tcx: TyCtxt<'tcx>,
id: DefIndex,
- ) -> IndexVec<Promoted, Body<'tcx>> {
- self.root.per_def.promoted_mir.get(self, id)
+ ) -> IndexVec<Promoted, BodyCache<'tcx>> {
+ let mut cache = self.root.per_def.promoted_mir.get(self, id)
.filter(|_| !self.is_proc_macro(id))
.unwrap_or_else(|| {
bug!("get_promoted_mir: missing MIR for `{:?}`", self.local_def_id(id))
})
- .decode((self, tcx))
+ .decode((self, tcx));
+ for body in cache.iter_mut() {
+ body.ensure_predecessors();
+ }
+ cache
}
fn mir_const_qualif(&self, id: DefIndex) -> mir::ConstQualifs {
use syntax::source_map::Spanned;
use syntax::symbol::Symbol;
use syntax::expand::allocator::AllocatorKind;
+use syntax::ptr::P;
+use syntax::tokenstream::DelimSpan;
use syntax_pos::{Span, FileName};
macro_rules! provide {
let source_file = sess.parse_sess.source_map().new_source_file(source_name, def.body);
let local_span = Span::with_root_ctxt(source_file.start_pos, source_file.end_pos);
+ let dspan = DelimSpan::from_single(local_span);
let (body, mut errors) = source_file_to_stream(&sess.parse_sess, source_file, None);
emit_unclosed_delims(&mut errors, &sess.parse_sess);
span: local_span,
attrs: attrs.iter().cloned().collect(),
kind: ast::ItemKind::MacroDef(ast::MacroDef {
- tokens: body.into(),
+ body: P(ast::MacArgs::Delimited(dspan, ast::MacDelimiter::Brace, body)),
legacy: def.legacy,
}),
vis: source_map::respan(local_span.shrink_to_lo(), ast::VisibilityKind::Inherited),
// Also, as an optimization, a missing entry indicates an empty `&[]`.
inferred_outlives: Table<DefIndex, Lazy!(&'tcx [(ty::Predicate<'tcx>, Span)])>,
super_predicates: Table<DefIndex, Lazy!(ty::GenericPredicates<'tcx>)>,
- mir: Table<DefIndex, Lazy!(mir::Body<'tcx>)>,
- promoted_mir: Table<DefIndex, Lazy!(IndexVec<mir::Promoted, mir::Body<'tcx>>)>,
+ mir: Table<DefIndex, Lazy!(mir::BodyCache<'tcx>)>,
+ promoted_mir: Table<DefIndex, Lazy!(IndexVec<mir::Promoted, mir::BodyCache<'tcx>>)>,
}
#[derive(Copy, Clone, RustcEncodable, RustcDecodable)]
use crate::dataflow::move_paths::MoveData;
use rustc::mir::traversal;
use rustc::mir::visit::{PlaceContext, Visitor, NonUseContext, MutatingUseContext};
-use rustc::mir::{self, Location, Body, Local};
+use rustc::mir::{self, Location, Body, Local, ReadOnlyBodyCache};
use rustc::ty::{RegionVid, TyCtxt};
use rustc::util::nodemap::{FxHashMap, FxHashSet};
use rustc_index::vec::IndexVec;
impl LocalsStateAtExit {
fn build(
locals_are_invalidated_at_exit: bool,
- body: &Body<'tcx>,
+ body: ReadOnlyBodyCache<'_, 'tcx>,
move_data: &MoveData<'tcx>
) -> Self {
struct HasStorageDead(BitSet<Local>);
if locals_are_invalidated_at_exit {
LocalsStateAtExit::AllAreInvalidated
} else {
- let mut has_storage_dead = HasStorageDead(BitSet::new_empty(body.local_decls.len()));
+ let mut has_storage_dead
+ = HasStorageDead(BitSet::new_empty(body.local_decls.len()));
has_storage_dead.visit_body(body);
let mut has_storage_dead_or_moved = has_storage_dead.0;
for move_out in &move_data.moves {
impl<'tcx> BorrowSet<'tcx> {
pub fn build(
tcx: TyCtxt<'tcx>,
- body: &Body<'tcx>,
+ body: ReadOnlyBodyCache<'_, 'tcx>,
locals_are_invalidated_at_exit: bool,
move_data: &MoveData<'tcx>,
) -> Self {
let mut visitor = GatherBorrows {
tcx,
- body,
+ body: &body,
idx_vec: IndexVec::new(),
location_map: Default::default(),
activation_map: Default::default(),
LocalsStateAtExit::build(locals_are_invalidated_at_exit, body, move_data),
};
- for (block, block_data) in traversal::preorder(body) {
+ for (block, block_data) in traversal::preorder(&body) {
visitor.visit_basic_block_data(block, block_data);
}
);
}
- let ty =
- Place::ty_from(used_place.base, used_place.projection, self.body, self.infcx.tcx)
- .ty;
+ let ty = Place::ty_from(
+ used_place.base,
+ used_place.projection,
+ &*self.body,
+ self.infcx.tcx
+ ).ty;
let needs_note = match ty.kind {
ty::Closure(id, _) => {
let tables = self.infcx.tcx.typeck_tables_of(id);
let mpi = self.move_data.moves[move_out_indices[0]].path;
let place = &self.move_data.move_paths[mpi].place;
- let ty = place.ty(self.body, self.infcx.tcx).ty;
+ let ty = place.ty(&*self.body, self.infcx.tcx).ty;
let opt_name =
self.describe_place_with_options(place.as_ref(), IncludingDowncast(true));
let note_msg = match opt_name {
None,
).add_explanation_to_diagnostic(
self.infcx.tcx,
- self.body,
+ &self.body,
&self.local_names,
&mut err,
"",
self.explain_why_borrow_contains_point(location, borrow, None)
.add_explanation_to_diagnostic(
self.infcx.tcx,
- self.body,
+ &self.body,
&self.local_names,
&mut err,
"",
explanation.add_explanation_to_diagnostic(
self.infcx.tcx,
- self.body,
+ &self.body,
&self.local_names,
&mut err,
first_borrow_desc,
// Define a small closure that we can use to check if the type of a place
// is a union.
let union_ty = |place_base, place_projection| {
- let ty = Place::ty_from(place_base, place_projection, self.body, self.infcx.tcx).ty;
+ let ty = Place::ty_from(
+ place_base,
+ place_projection,
+ &*self.body,
+ self.infcx.tcx
+ ).ty;
ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
};
let describe_place = |place| self.describe_place(place).unwrap_or_else(|| "_".to_owned());
} else {
explanation.add_explanation_to_diagnostic(
self.infcx.tcx,
- self.body,
+ &self.body,
&self.local_names,
&mut err,
"",
);
explanation.add_explanation_to_diagnostic(
- self.infcx.tcx, self.body, &self.local_names, &mut err, "", None);
+ self.infcx.tcx, &self.body, &self.local_names, &mut err, "", None);
}
err
explanation.add_explanation_to_diagnostic(
self.infcx.tcx,
- self.body,
+ &self.body,
&self.local_names,
&mut err,
"",
}
explanation.add_explanation_to_diagnostic(
self.infcx.tcx,
- self.body,
+ &self.body,
&self.local_names,
&mut err,
"",
};
// FIXME use a better heuristic than Spans
- let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
- "reference to"
- } else {
- "value referencing"
- };
+ let reference_desc
+ = if return_span == self.body.source_info(borrow.reserve_location).span {
+ "reference to"
+ } else {
+ "value referencing"
+ };
let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
}
fn get_moved_indexes(&mut self, location: Location, mpi: MovePathIndex) -> Vec<MoveSite> {
- let body = self.body;
-
let mut stack = Vec::new();
- stack.extend(body.predecessor_locations(location).map(|predecessor| {
+ stack.extend(self.body.predecessor_locations(location).map(|predecessor| {
let is_back_edge = location.dominates(predecessor, &self.dominators);
(predecessor, is_back_edge)
}));
}
// check for moves
- let stmt_kind = body[location.block]
+ let stmt_kind = self.body[location.block]
.statements
.get(location.statement_index)
.map(|s| &s.kind);
let mut any_match = false;
drop_flag_effects::for_location_inits(
self.infcx.tcx,
- self.body,
+ &self.body,
self.move_data,
location,
|m| {
continue 'dfs;
}
- stack.extend(body.predecessor_locations(location).map(|predecessor| {
+ stack.extend(self.body.predecessor_locations(location).map(|predecessor| {
let back_edge = location.dominates(predecessor, &self.dominators);
(predecessor, is_back_edge || back_edge)
}));
self.explain_why_borrow_contains_point(location, loan, None)
.add_explanation_to_diagnostic(
self.infcx.tcx,
- self.body,
+ &self.body,
&self.local_names,
&mut err,
"",
StorageDeadOrDrop::LocalStorageDead
| StorageDeadOrDrop::BoxedStorageDead => {
assert!(
- Place::ty_from(&place.base, proj_base, self.body, tcx).ty.is_box(),
+ Place::ty_from(
+ &place.base,
+ proj_base,
+ &*self.body,
+ tcx
+ ).ty.is_box(),
"Drop of value behind a reference or raw pointer"
);
StorageDeadOrDrop::BoxedStorageDead
StorageDeadOrDrop::Destructor(_) => base_access,
},
ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
- let base_ty = Place::ty_from(&place.base, proj_base, self.body, tcx).ty;
+ let base_ty = Place::ty_from(
+ &place.base,
+ proj_base,
+ &*self.body,
+ tcx
+ ).ty;
match base_ty.kind {
ty::Adt(def, _) if def.has_dtor(tcx) => {
// Report the outermost adt with a destructor
}
}
let mut visitor = FakeReadCauseFinder { place, cause: None };
- visitor.visit_body(&self.body);
+ visitor.visit_body(self.body);
match visitor.cause {
Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
Some(FakeReadCause::ForIndex) => Some("indexing expression"),
// Next, look through the rest of the block, checking if we are assigning the
// `target` (that is, the place that contains our borrow) to anything.
let mut annotated_closure = None;
- for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
+ for stmt in &self.body[location.block].statements[location.statement_index + 1..]
+ {
debug!(
"annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
target, stmt
}, field)
}
ProjectionElem::Downcast(_, variant_index) => {
- let base_ty =
- Place::ty_from(place.base, place.projection, self.body, self.infcx.tcx).ty;
+ let base_ty = Place::ty_from(
+ place.base,
+ place.projection,
+ &*self.body,
+ self.infcx.tcx).ty;
self.describe_field_from_ty(&base_ty, field, Some(*variant_index))
}
ProjectionElem::Field(_, field_type) => {
},
..
}) = bbd.terminator {
- if let Some(source)
- = BorrowedContentSource::from_call(func.ty(self.body, tcx), tcx)
- {
+ if let Some(source) = BorrowedContentSource::from_call(
+ func.ty(&*self.body, tcx),
+ tcx
+ ) {
return source;
}
}
// If we didn't find an overloaded deref or index, then assume it's a
// built in deref and check the type of the base.
- let base_ty = Place::ty_from(deref_base.base, deref_base.projection, self.body, tcx).ty;
+ let base_ty = Place::ty_from(
+ deref_base.base,
+ deref_base.projection,
+ &*self.body,
+ tcx
+ ).ty;
if base_ty.is_unsafe_ptr() {
BorrowedContentSource::DerefRawPointer
} else if base_ty.is_mutable_ptr() {
use rustc::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT};
use rustc::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
use rustc::mir::{
- ClearCrossCrate, Local, Location, Body, Mutability, Operand, Place, PlaceBase, PlaceElem,
- PlaceRef, Static, StaticKind
+ ClearCrossCrate, Local, Location, Body, BodyCache, Mutability, Operand, Place, PlaceBase,
+ PlaceElem, PlaceRef, ReadOnlyBodyCache, Static, StaticKind, read_only
};
use rustc::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind};
use rustc::mir::{Terminator, TerminatorKind};
fn do_mir_borrowck<'a, 'tcx>(
infcx: &InferCtxt<'a, 'tcx>,
input_body: &Body<'tcx>,
- input_promoted: &IndexVec<Promoted, Body<'tcx>>,
+ input_promoted: &IndexVec<Promoted, BodyCache<'tcx>>,
def_id: DefId,
) -> BorrowCheckResult<'tcx> {
debug!("do_mir_borrowck(def_id = {:?})", def_id);
// requires first making our own copy of the MIR. This copy will
// be modified (in place) to contain non-lexical lifetimes. It
// will have a lifetime tied to the inference context.
- let mut body: Body<'tcx> = input_body.clone();
- let mut promoted: IndexVec<Promoted, Body<'tcx>> = input_promoted.clone();
+ let body_clone: Body<'tcx> = input_body.clone();
+ let mut promoted = input_promoted.clone();
+ let mut body = BodyCache::new(body_clone);
let free_regions =
nll::replace_regions_in_mir(infcx, def_id, param_env, &mut body, &mut promoted);
- let body = &body; // no further changes
- let location_table = &LocationTable::new(body);
+ let body = read_only!(body); // no further changes
+ let promoted: IndexVec<_, _> = promoted
+ .iter_mut()
+ .map(|body| read_only!(body))
+ .collect();
+
+ let location_table = &LocationTable::new(&body);
let mut errors_buffer = Vec::new();
let (move_data, move_errors): (MoveData<'tcx>, Option<Vec<(Place<'tcx>, MoveError<'tcx>)>>) =
- match MoveData::gather_moves(body, tcx) {
+ match MoveData::gather_moves(&body, tcx) {
Ok(move_data) => (move_data, None),
Err((move_data, move_errors)) => (move_data, Some(move_errors)),
};
let dead_unwinds = BitSet::new_empty(body.basic_blocks().len());
let mut flow_inits = FlowAtLocation::new(do_dataflow(
tcx,
- body,
+ &body,
def_id,
&attributes,
&dead_unwinds,
- MaybeInitializedPlaces::new(tcx, body, &mdpe),
+ MaybeInitializedPlaces::new(tcx, &body, &mdpe),
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]),
));
let locals_are_invalidated_at_exit = tcx.hir().body_owner_kind(id).is_fn_or_closure();
let borrow_set = Rc::new(BorrowSet::build(
- tcx, body, locals_are_invalidated_at_exit, &mdpe.move_data));
+ tcx, body, locals_are_invalidated_at_exit, &mdpe.move_data));
// If we are in non-lexical mode, compute the non-lexical lifetimes.
let (regioncx, polonius_output, opt_closure_req) = nll::compute_regions(
let flow_borrows = FlowAtLocation::new(do_dataflow(
tcx,
- body,
+ &body,
def_id,
&attributes,
&dead_unwinds,
- Borrows::new(tcx, body, param_env, regioncx.clone(), &borrow_set),
+ Borrows::new(tcx, &body, param_env, regioncx.clone(), &borrow_set),
|rs, i| DebugFormatted::new(&rs.location(i)),
));
let flow_uninits = FlowAtLocation::new(do_dataflow(
tcx,
- body,
+ &body,
def_id,
&attributes,
&dead_unwinds,
- MaybeUninitializedPlaces::new(tcx, body, &mdpe),
+ MaybeUninitializedPlaces::new(tcx, &body, &mdpe),
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]),
));
let flow_ever_inits = FlowAtLocation::new(do_dataflow(
tcx,
- body,
+ &body,
def_id,
&attributes,
&dead_unwinds,
- EverInitializedPlaces::new(tcx, body, &mdpe),
+ EverInitializedPlaces::new(tcx, &body, &mdpe),
|bd, i| DebugFormatted::new(&bd.move_data().inits[i]),
));
let mut initial_diag =
mbcx.report_conflicting_borrow(location, (&place, span), bk, &borrow);
- let lint_root = if let ClearCrossCrate::Set(ref vsi) = mbcx.body.source_scope_local_data {
- let scope = mbcx.body.source_info(location).scope;
- vsi[scope].lint_root
- } else {
- id
+ let scope = mbcx.body.source_info(location).scope;
+ let lint_root = match &mbcx.body.source_scopes[scope].local_data {
+ ClearCrossCrate::Set(data) => data.lint_root,
+ _ => id,
};
// Span and message don't matter; we overwrite them below anyway
debug!("mbcx.used_mut: {:?}", mbcx.used_mut);
let used_mut = mbcx.used_mut;
- for local in mbcx.body.mut_vars_and_args_iter().filter(|local| !used_mut.contains(local)) {
- if let ClearCrossCrate::Set(ref vsi) = mbcx.body.source_scope_local_data {
- let local_decl = &mbcx.body.local_decls[local];
-
- // Skip over locals that begin with an underscore or have no name
- match mbcx.local_names[local] {
- Some(name) => if name.as_str().starts_with("_") {
- continue;
- },
- None => continue,
- }
+ for local in mbcx.body.mut_vars_and_args_iter().filter(|local| !used_mut.contains(local))
+ {
+ let local_decl = &mbcx.body.local_decls[local];
+ let lint_root = match &mbcx.body.source_scopes[local_decl.source_info.scope].local_data {
+ ClearCrossCrate::Set(data) => data.lint_root,
+ _ => continue,
+ };
- let span = local_decl.source_info.span;
- if span.desugaring_kind().is_some() {
- // If the `mut` arises as part of a desugaring, we should ignore it.
+ // Skip over locals that begin with an underscore or have no name
+ match mbcx.local_names[local] {
+ Some(name) => if name.as_str().starts_with("_") {
continue;
- }
+ },
+ None => continue,
+ }
- let mut_span = tcx.sess.source_map().span_until_non_whitespace(span);
- tcx.struct_span_lint_hir(
- UNUSED_MUT,
- vsi[local_decl.source_info.scope].lint_root,
- span,
- "variable does not need to be mutable",
- )
- .span_suggestion_short(
- mut_span,
- "remove this `mut`",
- String::new(),
- Applicability::MachineApplicable,
- )
- .emit();
+ let span = local_decl.source_info.span;
+ if span.desugaring_kind().is_some() {
+ // If the `mut` arises as part of a desugaring, we should ignore it.
+ continue;
}
+
+ let mut_span = tcx.sess.source_map().span_until_non_whitespace(span);
+ tcx.struct_span_lint_hir(
+ UNUSED_MUT,
+ lint_root,
+ span,
+ "variable does not need to be mutable",
+ )
+ .span_suggestion_short(
+ mut_span,
+ "remove this `mut`",
+ String::new(),
+ Applicability::MachineApplicable,
+ )
+ .emit();
}
// Buffer any move errors that we collected and de-duplicated.
crate struct MirBorrowckCtxt<'cx, 'tcx> {
crate infcx: &'cx InferCtxt<'cx, 'tcx>,
- body: &'cx Body<'tcx>,
+ body: ReadOnlyBodyCache<'cx, 'tcx>,
mir_def_id: DefId,
param_env: ty::ParamEnv<'tcx>,
move_data: &'cx MoveData<'tcx>,
type FlowState = Flows<'cx, 'tcx>;
fn body(&self) -> &'cx Body<'tcx> {
- self.body
+ self.body.body()
}
fn visit_block_entry(&mut self, bb: BasicBlock, flow_state: &Self::FlowState) {
let tcx = self.infcx.tcx;
// Compute the type with accurate region information.
- let drop_place_ty = drop_place.ty(self.body, self.infcx.tcx);
+ let drop_place_ty = drop_place.ty(&*self.body, self.infcx.tcx);
// Erase the regions.
let drop_place_ty = self.infcx.tcx.erase_regions(&drop_place_ty).ty;
let mut error_reported = false;
let tcx = self.infcx.tcx;
let body = self.body;
+ let body: &Body<'_> = &body;
let param_env = self.param_env;
let location_table = self.location_table.start_index(location);
let borrow_set = self.borrow_set.clone();
_ => bug!("temporary initialized in arguments"),
};
- let bbd = &self.body[loc.block];
+ let body = self.body;
+ let bbd = &body[loc.block];
let stmt = &bbd.statements[loc.statement_index];
debug!("temporary assigned in: stmt={:?}", stmt);
if places_conflict::borrow_conflicts_with_place(
self.infcx.tcx,
self.param_env,
- self.body,
+ &self.body,
place,
borrow.kind,
root_place,
// assigning to `P.f` requires `P` itself
// be already initialized
let tcx = self.infcx.tcx;
- let base_ty = Place::ty_from(&place.base, proj_base, self.body, tcx).ty;
+ let base_ty = Place::ty_from(&place.base, proj_base, self.body(), tcx).ty;
match base_ty.kind {
ty::Adt(def, _) if def.has_dtor(tcx) => {
self.check_if_path_or_subpath_is_moved(
// of the union - we should error in that case.
let tcx = this.infcx.tcx;
if let ty::Adt(def, _) =
- Place::ty_from(base.base, base.projection, this.body, tcx).ty.kind
+ Place::ty_from(base.base, base.projection, this.body(), tcx).ty.kind
{
if def.is_union() {
if this.move_data.path_map[mpi].iter().any(|moi| {
match elem {
ProjectionElem::Deref => {
let base_ty =
- Place::ty_from(place.base, proj_base, self.body, self.infcx.tcx).ty;
+ Place::ty_from(place.base, proj_base, self.body(), self.infcx.tcx).ty;
// Check the kind of deref to decide
match base_ty.kind {
match place_projection {
[base @ .., ProjectionElem::Field(field, _ty)] => {
let tcx = self.infcx.tcx;
- let base_ty = Place::ty_from(place_ref.base, base, self.body, tcx).ty;
+ let base_ty = Place::ty_from(place_ref.base, base, self.body(), tcx).ty;
if (base_ty.is_closure() || base_ty.is_generator()) &&
(!by_ref || self.upvars[field.index()].by_ref) {
// Inspect the type of the content behind the
// borrow to provide feedback about why this
// was a move rather than a copy.
- let ty = deref_target_place.ty(self.body, self.infcx.tcx).ty;
+ let ty = deref_target_place.ty(&*self.body, self.infcx.tcx).ty;
let upvar_field = self.prefixes(move_place.as_ref(), PrefixSet::All)
.find_map(|p| self.is_upvar_field_projection(p));
};
let move_ty = format!(
"{:?}",
- move_place.ty(self.body, self.infcx.tcx).ty,
+ move_place.ty(&*self.body, self.infcx.tcx).ty,
);
if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
let is_option = move_ty.starts_with("std::option::Option");
}
if binds_to.is_empty() {
- let place_ty = move_from.ty(self.body, self.infcx.tcx).ty;
+ let place_ty = move_from.ty(&*self.body, self.infcx.tcx).ty;
let place_desc = match self.describe_place(move_from.as_ref()) {
Some(desc) => format!("`{}`", desc),
None => format!("value"),
// No binding. Nothing to suggest.
GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => {
let span = use_spans.var_or_use();
- let place_ty = original_path.ty(self.body, self.infcx.tcx).ty;
+ let place_ty = original_path.ty(&*self.body, self.infcx.tcx).ty;
let place_desc = match self.describe_place(original_path.as_ref()) {
Some(desc) => format!("`{}`", desc),
None => format!("value"),
use rustc::hir;
use rustc::hir::Node;
-use rustc::mir::{self, Body, ClearCrossCrate, Local, LocalInfo, Location};
+use rustc::mir::{self, ClearCrossCrate, Local, LocalInfo, Location, ReadOnlyBodyCache};
use rustc::mir::{Mutability, Place, PlaceRef, PlaceBase, ProjectionElem};
use rustc::ty::{self, Ty, TyCtxt};
use rustc_index::vec::Idx;
projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
} => {
debug_assert!(is_closure_or_generator(
- Place::ty_from(&the_place_err.base, proj_base, self.body, self.infcx.tcx).ty
- ));
+ Place::ty_from(
+ &the_place_err.base,
+ proj_base,
+ &*self.body,
+ self.infcx.tcx
+ ).ty));
item_msg = format!("`{}`", access_place_desc.unwrap());
if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
Place::ty_from(
the_place_err.base,
the_place_err.projection,
- self.body,
+ &*self.body,
self.infcx.tcx
)
.ty
if let Some((span, message)) = annotate_struct_field(
self.infcx.tcx,
- Place::ty_from(base, proj_base, self.body, self.infcx.tcx).ty,
+ Place::ty_from(base, proj_base, &*self.body, self.infcx.tcx).ty,
field,
) {
err.span_suggestion(
projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
} => {
debug_assert!(is_closure_or_generator(
- Place::ty_from(base, proj_base, self.body, self.infcx.tcx).ty
+ Place::ty_from(base, proj_base, &*self.body, self.infcx.tcx).ty
));
err.span_label(span, format!("cannot {ACT}", ACT = act));
// by trying (3.), then (2.) and finally falling back on (1.).
fn suggest_ampmut<'tcx>(
tcx: TyCtxt<'tcx>,
- body: &Body<'tcx>,
+ body: ReadOnlyBodyCache<'_, 'tcx>,
local: Local,
local_decl: &mir::LocalDecl<'tcx>,
opt_ty_info: Option<Span>,
);
let regioncx = &self.nonlexical_regioncx;
- let body = self.body;
+ let body: &Body<'_> = &self.body;
let tcx = self.infcx.tcx;
let borrow_region_vid = borrow.region;
if let Some(region) = regioncx.to_error_region_vid(borrow_region_vid) {
let (category, from_closure, span, region_name) =
self.nonlexical_regioncx.free_region_constraint_info(
- self.body,
- &self.local_names,
- &self.upvars,
+ &self.body,
+ &self.local_names,
+ &self.upvars,
self.mir_def_id,
self.infcx,
borrow_region_vid,
use crate::dataflow::indexes::BorrowIndex;
use rustc::ty::{self, TyCtxt};
use rustc::mir::visit::Visitor;
-use rustc::mir::{BasicBlock, Location, Body, Place, Rvalue};
+use rustc::mir::{BasicBlock, Location, Body, Place, ReadOnlyBodyCache, Rvalue};
use rustc::mir::{Statement, StatementKind};
use rustc::mir::TerminatorKind;
use rustc::mir::{Operand, BorrowKind};
param_env: ty::ParamEnv<'tcx>,
all_facts: &mut Option<AllFacts>,
location_table: &LocationTable,
- body: &Body<'tcx>,
+ body: ReadOnlyBodyCache<'_, 'tcx>,
borrow_set: &BorrowSet<'tcx>,
) {
if all_facts.is_none() {
param_env,
tcx,
location_table,
- body,
+ body: &body,
dominators,
};
ig.visit_body(body);
use rustc::hir::def_id::DefId;
use rustc::infer::InferCtxt;
use rustc::mir::{ClosureOutlivesSubject, ClosureRegionRequirements,
- Local, Location, Body, LocalKind, BasicBlock, Promoted};
+ Local, Location, Body, BodyCache, LocalKind, BasicBlock,
+ Promoted, ReadOnlyBodyCache};
use rustc::ty::{self, RegionKind, RegionVid};
use rustc_index::vec::IndexVec;
use rustc_errors::Diagnostic;
infcx: &InferCtxt<'cx, 'tcx>,
def_id: DefId,
param_env: ty::ParamEnv<'tcx>,
- body: &mut Body<'tcx>,
- promoted: &mut IndexVec<Promoted, Body<'tcx>>,
+ body: &mut BodyCache<'tcx>,
+ promoted: &mut IndexVec<Promoted, BodyCache<'tcx>>,
) -> UniversalRegions<'tcx> {
debug!("replace_regions_in_mir(def_id={:?})", def_id);
infcx: &InferCtxt<'cx, 'tcx>,
def_id: DefId,
universal_regions: UniversalRegions<'tcx>,
- body: &Body<'tcx>,
- promoted: &IndexVec<Promoted, Body<'tcx>>,
+ body: ReadOnlyBodyCache<'_, 'tcx>,
+ promoted: &IndexVec<Promoted, ReadOnlyBodyCache<'_, 'tcx>>,
local_names: &IndexVec<Local, Option<Symbol>>,
upvars: &[Upvar],
location_table: &LocationTable,
let universal_regions = Rc::new(universal_regions);
- let elements = &Rc::new(RegionValueElements::new(body));
+ let elements
+ = &Rc::new(RegionValueElements::new(&body));
// Run the MIR type-checker.
let MirTypeckResults {
all_facts
.universal_region
.extend(universal_regions.universal_regions());
- populate_polonius_move_facts(all_facts, move_data, location_table, body);
+ populate_polonius_move_facts(all_facts, move_data, location_table, &body);
}
// Create the region inference context, taking ownership of the
universal_regions,
placeholder_indices,
universal_region_relations,
- body,
outlives_constraints,
member_constraints,
closure_bounds_mapping,
param_env,
&mut all_facts,
location_table,
- &body,
+ body,
borrow_set,
);
// Solve the region constraints.
let closure_region_requirements =
- regioncx.solve(infcx, body, local_names, upvars, def_id, errors_buffer);
+ regioncx.solve(infcx, &body, local_names, upvars, def_id, errors_buffer);
// Dump MIR results into a file, if that is enabled. This let us
// write unit-tests, as well as helping with debugging.
// We also have a `#[rustc_nll]` annotation that causes us to dump
// information
- dump_annotation(infcx, &body, def_id, ®ioncx, &closure_region_requirements, errors_buffer);
+ dump_annotation(
+ infcx,
+ &body,
+ def_id,
+ ®ioncx,
+ &closure_region_requirements,
+ errors_buffer);
(regioncx, polonius_output, closure_region_requirements)
}
universal_regions: Rc<UniversalRegions<'tcx>>,
placeholder_indices: Rc<PlaceholderIndices>,
universal_region_relations: Rc<UniversalRegionRelations<'tcx>>,
- _body: &Body<'tcx>,
outlives_constraints: OutlivesConstraintSet,
member_constraints_in: MemberConstraintSet<'tcx, RegionVid>,
closure_bounds_mapping: FxHashMap<
-use rustc::mir::{BasicBlock, Location, Body};
+use rustc::mir::{BasicBlock, Location, Body, ReadOnlyBodyCache};
use rustc::ty::{self, RegionVid};
use rustc_index::bit_set::{HybridBitSet, SparseBitMatrix};
use rustc_data_structures::fx::FxHashMap;
/// Pushes all predecessors of `index` onto `stack`.
crate fn push_predecessors(
&self,
- body: &Body<'_>,
+ body: ReadOnlyBodyCache<'_, '_>,
index: PointIndex,
stack: &mut Vec<PointIndex>,
) {
use rustc::ty::subst::SubstsRef;
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
-use rustc::mir::{Body, Location, PlaceElem, Promoted};
+use rustc::mir::{BodyCache, Location, PlaceElem, Promoted};
use rustc::mir::visit::{MutVisitor, TyContext};
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
use rustc_index::vec::IndexVec;
/// inference variables, returning the number of variables created.
pub fn renumber_mir<'tcx>(
infcx: &InferCtxt<'_, 'tcx>,
- body: &mut Body<'tcx>,
- promoted: &mut IndexVec<Promoted, Body<'tcx>>,
+ body: &mut BodyCache<'tcx>,
+ promoted: &mut IndexVec<Promoted, BodyCache<'tcx>>,
) {
debug!("renumber_mir()");
debug!("renumber_mir: body.arg_count={:?}", body.arg_count);
use crate::borrow_check::nll::region_infer::values::{PointIndex, RegionValueElements};
use crate::util::liveness::{categorize, DefUse};
use rustc::mir::visit::{PlaceContext, Visitor};
-use rustc::mir::{Body, Local, Location};
+use rustc::mir::{Local, Location, ReadOnlyBodyCache};
use rustc_index::vec::{Idx, IndexVec};
use rustc_data_structures::vec_linked_list as vll;
crate fn build(
live_locals: &Vec<Local>,
elements: &RegionValueElements,
- body: &Body<'_>,
+ body: ReadOnlyBodyCache<'_, '_>,
) -> Self {
let nones = IndexVec::from_elem_n(None, body.local_decls.len());
let mut local_use_map = LocalUseMap {
use crate::dataflow::move_paths::MoveData;
use crate::dataflow::FlowAtLocation;
use crate::dataflow::MaybeInitializedPlaces;
-use rustc::mir::{Body, Local};
+use rustc::mir::{Body, Local, ReadOnlyBodyCache};
use rustc::ty::{RegionVid, TyCtxt};
use rustc_data_structures::fx::FxHashSet;
use std::rc::Rc;
/// performed before
pub(super) fn generate<'tcx>(
typeck: &mut TypeChecker<'_, 'tcx>,
- body: &Body<'tcx>,
+ body: ReadOnlyBodyCache<'_, 'tcx>,
elements: &Rc<RegionValueElements>,
flow_inits: &mut FlowAtLocation<'tcx, MaybeInitializedPlaces<'_, 'tcx>>,
move_data: &MoveData<'tcx>,
&typeck.borrowck_context.universal_regions,
&typeck.borrowck_context.constraints.outlives_constraints,
);
- let live_locals = compute_live_locals(typeck.tcx(), &free_regions, body);
+ let live_locals = compute_live_locals(typeck.tcx(), &free_regions, &body);
let facts_enabled = AllFacts::enabled(typeck.tcx());
-
let polonius_drop_used = if facts_enabled {
let mut drop_used = Vec::new();
polonius::populate_access_facts(
use crate::dataflow::move_paths::{LookupResult, MoveData};
use crate::util::liveness::{categorize, DefUse};
use rustc::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
-use rustc::mir::{Body, Local, Location, Place};
+use rustc::mir::{Local, Location, Place, ReadOnlyBodyCache};
use rustc::ty::subst::GenericArg;
use rustc::ty::Ty;
pub(super) fn populate_access_facts(
typeck: &mut TypeChecker<'_, 'tcx>,
- body: &Body<'tcx>,
+ body: ReadOnlyBodyCache<'_, 'tcx>,
location_table: &LocationTable,
move_data: &MoveData<'_>,
drop_used: &mut Vec<(Local, Location)>,
use crate::dataflow::move_paths::MoveData;
use crate::dataflow::{FlowAtLocation, FlowsAtLocation, MaybeInitializedPlaces};
use rustc::infer::canonical::QueryRegionConstraints;
-use rustc::mir::{BasicBlock, Body, ConstraintCategory, Local, Location};
+use rustc::mir::{BasicBlock, ConstraintCategory, Local, Location, ReadOnlyBodyCache};
use rustc::traits::query::dropck_outlives::DropckOutlivesResult;
use rustc::traits::query::type_op::outlives::DropckOutlives;
use rustc::traits::query::type_op::TypeOp;
/// this respects `#[may_dangle]` annotations).
pub(super) fn trace(
typeck: &mut TypeChecker<'_, 'tcx>,
- body: &Body<'tcx>,
+ body: ReadOnlyBodyCache<'_, 'tcx>,
elements: &Rc<RegionValueElements>,
flow_inits: &mut FlowAtLocation<'tcx, MaybeInitializedPlaces<'_, 'tcx>>,
move_data: &MoveData<'tcx>,
elements: &'me RegionValueElements,
/// MIR we are analyzing.
- body: &'me Body<'tcx>,
+ body: ReadOnlyBodyCache<'me, 'tcx>,
/// Mapping to/from the various indices used for initialization tracking.
move_data: &'me MoveData<'tcx>,
}
}
- for &pred_block in self.cx.body.predecessors_for(block).iter() {
+ let body = self.cx.body;
+ for &pred_block in body.predecessors_for(block).iter() {
debug!("compute_drop_live_points_for_block: pred_block = {:?}", pred_block,);
// Check whether the variable is (at least partially)
//! This pass type-checks the MIR to ensure it is not broken.
-use crate::borrow_check::borrow_set::BorrowSet;
-use crate::borrow_check::location::LocationTable;
-use crate::borrow_check::nll::constraints::{OutlivesConstraintSet, OutlivesConstraint};
-use crate::borrow_check::nll::member_constraints::MemberConstraintSet;
-use crate::borrow_check::nll::facts::AllFacts;
-use crate::borrow_check::nll::region_infer::values::LivenessValues;
-use crate::borrow_check::nll::region_infer::values::PlaceholderIndex;
-use crate::borrow_check::nll::region_infer::values::PlaceholderIndices;
-use crate::borrow_check::nll::region_infer::values::RegionValueElements;
-use crate::borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, TypeTest};
-use crate::borrow_check::nll::renumber;
-use crate::borrow_check::nll::type_check::free_region_relations::{
- CreateResult, UniversalRegionRelations,
-};
-use crate::borrow_check::nll::universal_regions::{DefiningTy, UniversalRegions};
-use crate::borrow_check::nll::ToRegionVid;
-use crate::transform::promote_consts::should_suggest_const_in_array_repeat_expressions_attribute;
-use crate::dataflow::move_paths::MoveData;
-use crate::dataflow::FlowAtLocation;
-use crate::dataflow::MaybeInitializedPlaces;
+use std::{fmt, iter, mem};
+use std::rc::Rc;
+
use either::Either;
+
use rustc::hir;
use rustc::hir::def_id::DefId;
+use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionVariableOrigin};
use rustc::infer::canonical::QueryRegionConstraints;
use rustc::infer::outlives::env::RegionBoundPairs;
-use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionVariableOrigin};
use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc::mir::*;
use rustc::mir::interpret::PanicInfo;
use rustc::mir::tcx::PlaceTy;
-use rustc::mir::visit::{PlaceContext, Visitor, NonMutatingUseContext};
-use rustc::mir::*;
+use rustc::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
+use rustc::traits::{self, ObligationCause, PredicateObligations};
+use rustc::traits::query::{Fallible, NoSolution};
use rustc::traits::query::type_op;
use rustc::traits::query::type_op::custom::CustomTypeOp;
-use rustc::traits::query::{Fallible, NoSolution};
-use rustc::traits::{self, ObligationCause, PredicateObligations};
-use rustc::ty::adjustment::{PointerCast};
-use rustc::ty::cast::CastTy;
-use rustc::ty::fold::TypeFoldable;
-use rustc::ty::subst::{Subst, SubstsRef, GenericArgKind, UserSubsts};
use rustc::ty::{
- self, RegionVid, ToPolyTraitRef, Ty, TyCtxt, UserType,
- CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
+ self, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, RegionVid, ToPolyTraitRef, Ty,
+ TyCtxt, UserType,
UserTypeAnnotationIndex,
};
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_index::vec::{IndexVec, Idx};
+use rustc::ty::adjustment::PointerCast;
+use rustc::ty::cast::CastTy;
+use rustc::ty::fold::TypeFoldable;
use rustc::ty::layout::VariantIdx;
-use std::rc::Rc;
-use std::{fmt, iter, mem};
-use syntax_pos::{Span, DUMMY_SP};
-
+use rustc::ty::subst::{GenericArgKind, Subst, SubstsRef, UserSubsts};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_error_codes::*;
+use rustc_index::vec::{Idx, IndexVec};
+use syntax_pos::{DUMMY_SP, Span};
+
+use crate::borrow_check::borrow_set::BorrowSet;
+use crate::borrow_check::location::LocationTable;
+use crate::borrow_check::nll::constraints::{OutlivesConstraint, OutlivesConstraintSet};
+use crate::borrow_check::nll::facts::AllFacts;
+use crate::borrow_check::nll::member_constraints::MemberConstraintSet;
+use crate::borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, TypeTest};
+use crate::borrow_check::nll::region_infer::values::LivenessValues;
+use crate::borrow_check::nll::region_infer::values::PlaceholderIndex;
+use crate::borrow_check::nll::region_infer::values::PlaceholderIndices;
+use crate::borrow_check::nll::region_infer::values::RegionValueElements;
+use crate::borrow_check::nll::renumber;
+use crate::borrow_check::nll::ToRegionVid;
+use crate::borrow_check::nll::type_check::free_region_relations::{
+ CreateResult, UniversalRegionRelations,
+};
+use crate::borrow_check::nll::universal_regions::{DefiningTy, UniversalRegions};
+use crate::dataflow::FlowAtLocation;
+use crate::dataflow::MaybeInitializedPlaces;
+use crate::dataflow::move_paths::MoveData;
+use crate::transform::promote_consts::should_suggest_const_in_array_repeat_expressions_attribute;
macro_rules! span_mirbug {
($context:expr, $elem:expr, $($message:tt)*) => ({
pub(crate) fn type_check<'tcx>(
infcx: &InferCtxt<'_, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
- body: &Body<'tcx>,
- promoted: &IndexVec<Promoted, Body<'tcx>>,
+ body: ReadOnlyBodyCache<'_, 'tcx>,
+ promoted: &IndexVec<Promoted, ReadOnlyBodyCache<'_, 'tcx>>,
mir_def_id: DefId,
universal_regions: &Rc<UniversalRegions<'tcx>>,
location_table: &LocationTable,
&mut borrowck_context,
&universal_region_relations,
|mut cx| {
- cx.equate_inputs_and_outputs(body, universal_regions, &normalized_inputs_and_output);
- liveness::generate(&mut cx, body, elements, flow_inits, move_data, location_table);
+ cx.equate_inputs_and_outputs(
+ &body,
+ universal_regions,
+ &normalized_inputs_and_output);
+ liveness::generate(
+ &mut cx,
+ body,
+ elements,
+ flow_inits,
+ move_data,
+ location_table);
translate_outlives_facts(cx.borrowck_context);
},
infcx: &'a InferCtxt<'a, 'tcx>,
mir_def_id: DefId,
param_env: ty::ParamEnv<'tcx>,
- body: &'a Body<'tcx>,
- promoted: &'a IndexVec<Promoted, Body<'tcx>>,
+ body: ReadOnlyBodyCache<'a, 'tcx>,
+ promoted: &'a IndexVec<Promoted, ReadOnlyBodyCache<'_, 'tcx>>,
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
implicit_region_bound: ty::Region<'tcx>,
borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>,
universal_region_relations: &'a UniversalRegionRelations<'tcx>,
mut extra: impl FnMut(&mut TypeChecker<'a, 'tcx>) -> R,
-) -> R where {
+) -> R {
let mut checker = TypeChecker::new(
infcx,
- body,
+ body.body(),
mir_def_id,
param_env,
region_bound_pairs,
universal_region_relations,
);
let errors_reported = {
- let mut verifier = TypeVerifier::new(&mut checker, body, promoted);
+ let mut verifier = TypeVerifier::new(&mut checker, body.body(), promoted);
verifier.visit_body(body);
verifier.errors_reported
};
struct TypeVerifier<'a, 'b, 'tcx> {
cx: &'a mut TypeChecker<'b, 'tcx>,
body: &'b Body<'tcx>,
- promoted: &'b IndexVec<Promoted, Body<'tcx>>,
+ promoted: &'b IndexVec<Promoted, ReadOnlyBodyCache<'b, 'tcx>>,
last_span: Span,
mir_def_id: DefId,
errors_reported: bool,
}
}
- fn visit_body(&mut self, body: &Body<'tcx>) {
+ fn visit_body(&mut self, body: ReadOnlyBodyCache<'_, 'tcx>) {
self.sanitize_type(&"return type", body.return_ty());
for local_decl in &body.local_decls {
self.sanitize_type(local_decl, local_decl.ty);
fn new(
cx: &'a mut TypeChecker<'b, 'tcx>,
body: &'b Body<'tcx>,
- promoted: &'b IndexVec<Promoted, Body<'tcx>>,
+ promoted: &'b IndexVec<Promoted, ReadOnlyBodyCache<'b, 'tcx>>,
) -> Self {
TypeVerifier {
body,
match kind {
StaticKind::Promoted(promoted, _) => {
if !self.errors_reported {
- let promoted_body = &self.promoted[*promoted];
- self.sanitize_promoted(promoted_body, location);
+ let promoted_body_cache = self.promoted[*promoted];
+ self.sanitize_promoted(promoted_body_cache, location);
- let promoted_ty = promoted_body.return_ty();
+ let promoted_ty = promoted_body_cache.return_ty();
check_err(self, place, promoted_ty, san_ty);
}
}
place_ty
}
- fn sanitize_promoted(&mut self, promoted_body: &'b Body<'tcx>, location: Location) {
+ fn sanitize_promoted(
+ &mut self,
+ promoted_body: ReadOnlyBodyCache<'b, 'tcx>,
+ location: Location
+ ) {
// Determine the constraints from the promoted MIR by running the type
// checker on the promoted MIR, then transfer the constraints back to
// the main MIR, changing the locations to the provided location.
- let parent_body = mem::replace(&mut self.body, promoted_body);
+ let parent_body = mem::replace(&mut self.body, promoted_body.body());
// Use new sets of constraints and closure bounds so that we can
// modify their locations.
let mut constraints = Default::default();
let mut closure_bounds = Default::default();
let mut liveness_constraints = LivenessValues::new(
- Rc::new(RegionValueElements::new(promoted_body)),
+ Rc::new(RegionValueElements::new(&promoted_body)),
);
// Don't try to add borrow_region facts for the promoted MIR
self.infcx.tcx
}
- fn check_stmt(&mut self, body: &Body<'tcx>, stmt: &Statement<'tcx>, location: Location) {
+ fn check_stmt(
+ &mut self,
+ body: ReadOnlyBodyCache<'_, 'tcx>,
+ stmt: &Statement<'tcx>,
+ location: Location)
+ {
debug!("check_stmt: {:?}", stmt);
let tcx = self.tcx();
match stmt.kind {
_ => ConstraintCategory::Assignment,
};
- let place_ty = place.ty(body, tcx).ty;
+ let place_ty = place.ty(&*body, tcx).ty;
let place_ty = self.normalize(place_ty, location);
- let rv_ty = rv.ty(body, tcx);
+ let rv_ty = rv.ty(&*body, tcx);
let rv_ty = self.normalize(rv_ty, location);
if let Err(terr) =
self.sub_types_or_anon(rv_ty, place_ty, location.to_locations(), category)
ref place,
variant_index,
} => {
- let place_type = place.ty(body, tcx).ty;
+ let place_type = place.ty(&*body, tcx).ty;
let adt = match place_type.kind {
ty::Adt(adt, _) if adt.is_enum() => adt,
_ => {
};
}
StatementKind::AscribeUserType(box(ref place, ref projection), variance) => {
- let place_ty = place.ty(body, tcx).ty;
+ let place_ty = place.ty(&*body, tcx).ty;
if let Err(terr) = self.relate_type_and_user_type(
place_ty,
variance,
}
}
- fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
+ fn check_rvalue(
+ &mut self,
+ body: ReadOnlyBodyCache<'_, 'tcx>,
+ rvalue: &Rvalue<'tcx>,
+ location: Location)
+ {
let tcx = self.tcx();
match rvalue {
Rvalue::Aggregate(ak, ops) => {
- self.check_aggregate_rvalue(body, rvalue, ak, ops, location)
+ self.check_aggregate_rvalue(&body, rvalue, ak, ops, location)
}
Rvalue::Repeat(operand, len) => if *len > 1 {
// While this is located in `nll::typeck` this error is not an NLL error, it's
// a required check to make sure that repeated elements implement `Copy`.
let span = body.source_info(location).span;
- let ty = operand.ty(body, tcx);
+ let ty = operand.ty(&*body, tcx);
if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) {
// To determine if `const_in_array_repeat_expressions` feature gate should
// be mentioned, need to check if the rvalue is promotable.
Rvalue::Cast(cast_kind, op, ty) => {
match cast_kind {
CastKind::Pointer(PointerCast::ReifyFnPointer) => {
- let fn_sig = op.ty(body, tcx).fn_sig(tcx);
+ let fn_sig = op.ty(&*body, tcx).fn_sig(tcx);
// The type that we see in the fcx is like
// `foo::<'a, 'b>`, where `foo` is the path to a
}
CastKind::Pointer(PointerCast::ClosureFnPointer(unsafety)) => {
- let sig = match op.ty(body, tcx).kind {
+ let sig = match op.ty(&*body, tcx).kind {
ty::Closure(def_id, substs) => {
substs.as_closure().sig_ty(def_id, tcx).fn_sig(tcx)
}
}
CastKind::Pointer(PointerCast::UnsafeFnPointer) => {
- let fn_sig = op.ty(body, tcx).fn_sig(tcx);
+ let fn_sig = op.ty(&*body, tcx).fn_sig(tcx);
// The type that we see in the fcx is like
// `foo::<'a, 'b>`, where `foo` is the path to a
let &ty = ty;
let trait_ref = ty::TraitRef {
def_id: tcx.lang_items().coerce_unsized_trait().unwrap(),
- substs: tcx.mk_substs_trait(op.ty(body, tcx), &[ty.into()]),
+ substs: tcx.mk_substs_trait(op.ty(&*body, tcx), &[ty.into()]),
};
self.prove_trait_ref(
}
CastKind::Pointer(PointerCast::MutToConstPointer) => {
- let ty_from = match op.ty(body, tcx).kind {
+ let ty_from = match op.ty(&*body, tcx).kind {
ty::RawPtr(ty::TypeAndMut {
ty: ty_from,
mutbl: hir::Mutability::Mutable,
}
CastKind::Pointer(PointerCast::ArrayToPointer) => {
- let ty_from = op.ty(body, tcx);
+ let ty_from = op.ty(&*body, tcx);
let opt_ty_elem = match ty_from.kind {
ty::RawPtr(
}
CastKind::Misc => {
- let ty_from = op.ty(body, tcx);
+ let ty_from = op.ty(&*body, tcx);
let cast_ty_from = CastTy::from_ty(ty_from);
let cast_ty_to = CastTy::from_ty(ty);
match (cast_ty_from, cast_ty_to) {
}
Rvalue::Ref(region, _borrow_kind, borrowed_place) => {
- self.add_reborrow_constraint(body, location, region, borrowed_place);
+ self.add_reborrow_constraint(&body, location, region, borrowed_place);
}
Rvalue::BinaryOp(BinOp::Eq, left, right)
| Rvalue::BinaryOp(BinOp::Le, left, right)
| Rvalue::BinaryOp(BinOp::Gt, left, right)
| Rvalue::BinaryOp(BinOp::Ge, left, right) => {
- let ty_left = left.ty(body, tcx);
+ let ty_left = left.ty(&*body, tcx);
if let ty::RawPtr(_) | ty::FnPtr(_) = ty_left.kind {
- let ty_right = right.ty(body, tcx);
+ let ty_right = right.ty(&*body, tcx);
let common_ty = self.infcx.next_ty_var(
TypeVariableOrigin {
kind: TypeVariableOriginKind::MiscVariable,
})
}
- fn typeck_mir(&mut self, body: &Body<'tcx>) {
+ fn typeck_mir(&mut self, body: ReadOnlyBodyCache<'_, 'tcx>) {
self.last_span = body.span;
debug!("run_on_mir: {:?}", body.span);
for (local, local_decl) in body.local_decls.iter_enumerated() {
- self.check_local(body, local, local_decl);
+ self.check_local(&body, local, local_decl);
}
for (block, block_data) in body.basic_blocks().iter_enumerated() {
location.statement_index += 1;
}
- self.check_terminator(body, block_data.terminator(), location);
- self.check_iscleanup(body, block_data);
+ self.check_terminator(&body, block_data.terminator(), location);
+ self.check_iscleanup(&body, block_data);
}
}
use rustc::hir;
use rustc::ty::{self, TyCtxt};
-use rustc::mir::{Body, Place, PlaceBase, PlaceRef, ProjectionElem};
+use rustc::mir::{Place, PlaceBase, PlaceRef, ProjectionElem, ReadOnlyBodyCache};
pub trait IsPrefixOf<'cx, 'tcx> {
fn is_prefix_of(&self, other: PlaceRef<'cx, 'tcx>) -> bool;
}
pub(super) struct Prefixes<'cx, 'tcx> {
- body: &'cx Body<'tcx>,
+ body: ReadOnlyBodyCache<'cx, 'tcx>,
tcx: TyCtxt<'tcx>,
kind: PrefixSet,
next: Option<PlaceRef<'cx, 'tcx>>,
// derefs, except we stop at the deref of a shared
// reference.
- let ty = Place::ty_from(cursor.base, proj_base, self.body, self.tcx).ty;
+ let ty = Place::ty_from(cursor.base, proj_base, &*self.body, self.tcx).ty;
match ty.kind {
ty::RawPtr(_) |
ty::Ref(
use super::lints;
/// Construct the MIR for a given `DefId`.
-pub fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> Body<'_> {
+pub fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> BodyCache<'_> {
let id = tcx.hir().as_local_hir_id(def_id).unwrap();
// Figure out what primary body this item has.
lints::check(tcx, &body, def_id);
+ let mut body = BodyCache::new(body);
+ body.ensure_predecessors();
body
})
}
/// The vector of all scopes that we have created thus far;
/// we track this for debuginfo later.
source_scopes: IndexVec<SourceScope, SourceScopeData>,
- source_scope_local_data: IndexVec<SourceScope, SourceScopeLocalData>,
source_scope: SourceScope,
/// The guard-context: each time we build the guard expression for
block_context: BlockContext::new(),
source_scopes: IndexVec::new(),
source_scope: OUTERMOST_SOURCE_SCOPE,
- source_scope_local_data: IndexVec::new(),
guard_context: vec![],
push_unsafe_count: 0,
unpushed_unsafe: safety,
Body::new(
self.cfg.basic_blocks,
self.source_scopes,
- ClearCrossCrate::Set(self.source_scope_local_data),
self.local_decls,
self.canonical_user_type_annotations,
self.arg_count,
self.hir.root_lint_level
);
let parent_root = tcx.maybe_lint_level_root_bounded(
- self.source_scope_local_data[original_source_scope].lint_root,
+ self.source_scopes[original_source_scope]
+ .local_data
+ .as_ref()
+ .assert_crate_local()
+ .lint_root,
self.hir.root_lint_level,
);
if current_root != parent_root {
// We estimate the true lint roots here to avoid creating a lot of source scopes.
let parent_root = tcx.maybe_lint_level_root_bounded(
- self.source_scope_local_data[source_scope].lint_root,
+ self.source_scopes[source_scope]
+ .local_data
+ .as_ref()
+ .assert_crate_local()
+ .lint_root,
self.hir.root_lint_level,
);
let current_root = tcx.maybe_lint_level_root_bounded(
let parent = self.source_scope;
debug!("new_source_scope({:?}, {:?}, {:?}) - parent({:?})={:?}",
span, lint_level, safety,
- parent, self.source_scope_local_data.get(parent));
- let scope = self.source_scopes.push(SourceScopeData {
- span,
- parent_scope: Some(parent),
- });
+ parent, self.source_scopes.get(parent));
let scope_local_data = SourceScopeLocalData {
lint_root: if let LintLevel::Explicit(lint_root) = lint_level {
lint_root
} else {
- self.source_scope_local_data[parent].lint_root
+ self.source_scopes[parent].local_data.as_ref().assert_crate_local().lint_root
},
safety: safety.unwrap_or_else(|| {
- self.source_scope_local_data[parent].safety
+ self.source_scopes[parent].local_data.as_ref().assert_crate_local().safety
})
};
- self.source_scope_local_data.push(scope_local_data);
- scope
+ self.source_scopes.push(SourceScopeData {
+ span,
+ parent_scope: Some(parent),
+ local_data: ClearCrossCrate::Set(scope_local_data),
+ })
}
/// Given a span and the current source scope, make a SourceInfo.
use crate::interpret::{self,
PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar, Pointer,
RawConst, ConstValue, Machine,
- InterpResult, InterpErrorInfo, GlobalId, InterpCx, StackPopCleanup,
+ InterpResult, InterpErrorInfo, GlobalId, InterpCx, StackPopCleanup, AssertMessage,
Allocation, AllocId, MemoryKind, Memory,
snapshot, RefTracking, intern_const_alloc_recursive,
};
}
// This is a const fn. Call it.
Ok(Some(match ecx.load_mir(instance.def, None) {
- Ok(body) => body,
+ Ok(body) => body.body(),
Err(err) => {
if let err_unsup!(NoMirFor(ref path)) = err.kind {
return Err(
)
}
+ fn assert_panic(
+ ecx: &mut InterpCx<'mir, 'tcx, Self>,
+ _span: Span,
+ msg: &AssertMessage<'tcx>,
+ _unwind: Option<mir::BasicBlock>,
+ ) -> InterpResult<'tcx> {
+ use rustc::mir::interpret::PanicInfo::*;
+ Err(match msg {
+ BoundsCheck { ref len, ref index } => {
+ let len = ecx
+ .read_immediate(ecx.eval_operand(len, None)?)
+ .expect("can't eval len")
+ .to_scalar()?
+ .to_machine_usize(&*ecx)?;
+ let index = ecx
+ .read_immediate(ecx.eval_operand(index, None)?)
+ .expect("can't eval index")
+ .to_scalar()?
+ .to_machine_usize(&*ecx)?;
+ err_panic!(BoundsCheck { len, index })
+ }
+ Overflow(op) => err_panic!(Overflow(*op)),
+ OverflowNeg => err_panic!(OverflowNeg),
+ DivisionByZero => err_panic!(DivisionByZero),
+ RemainderByZero => err_panic!(RemainderByZero),
+ ResumedAfterReturn(generator_kind)
+ => err_panic!(ResumedAfterReturn(*generator_kind)),
+ ResumedAfterPanic(generator_kind)
+ => err_panic!(ResumedAfterPanic(*generator_kind)),
+ Panic { .. } => bug!("`Panic` variant cannot occur in MIR"),
+ }
+ .into())
+ }
+
fn ptr_to_int(
_mem: &Memory<'mir, 'tcx, Self>,
_ptr: Pointer,
}
#[inline(always)]
- fn tag_allocation<'b>(
+ fn init_allocation_extra<'b>(
_memory_extra: &(),
_id: AllocId,
alloc: Cow<'b, Allocation>,
tcx.type_of(tcx.require_lang_item(PanicLocationLangItem, None))
.subst(tcx, tcx.mk_substs([tcx.lifetimes.re_static.into()].iter())),
);
- let loc_place = ecx.alloc_caller_location(file, line, col).unwrap();
+ let loc_place = ecx.alloc_caller_location(file, line, col);
intern_const_alloc_recursive(&mut ecx, None, loc_place).unwrap();
let loc_const = ty::Const {
ty: loc_ty,
let res = ecx.load_mir(cid.instance.def, cid.promoted);
res.and_then(
- |body| eval_body_using_ecx(&mut ecx, cid, body)
+ |body| eval_body_using_ecx(&mut ecx, cid, body.body())
).and_then(|place| {
Ok(RawConst {
alloc_id: place.ptr.assert_ptr().alloc_id,
// Add successor BBs to the work list, if necessary.
let bb_data = &body[bb];
assert!(hi == bb_data.statements.len());
- for &succ_bb in bb_data.terminator.as_ref().unwrap().successors() {
+ for &succ_bb in bb_data.terminator().successors() {
visited.entry(succ_bb)
.and_modify(|lo| {
// `succ_bb` has been seen before. If it wasn't
}
Borrows {
- tcx: tcx,
- body: body,
+ tcx,
+ body,
param_env,
borrow_set: borrow_set.clone(),
borrows_out_of_scope_at_location,
impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
- MaybeInitializedPlaces { tcx: tcx, body: body, mdpe: mdpe }
+ MaybeInitializedPlaces { tcx, body, mdpe }
}
}
impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
- MaybeUninitializedPlaces { tcx: tcx, body: body, mdpe: mdpe }
+ MaybeUninitializedPlaces { tcx, body, mdpe }
}
}
impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
- DefinitelyInitializedPlaces { tcx: tcx, body: body, mdpe: mdpe }
+ DefinitelyInitializedPlaces { tcx, body, mdpe }
}
}
impl<'a, 'tcx> EverInitializedPlaces<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
- EverInitializedPlaces { tcx: tcx, body: body, mdpe: mdpe }
+ EverInitializedPlaces { tcx, body, mdpe }
}
}
/// Dataflow analysis that determines whether each local requires storage at a
/// given location; i.e. whether its storage can go away without being observed.
pub struct RequiresStorage<'mir, 'tcx> {
- body: &'mir Body<'tcx>,
+ body: ReadOnlyBodyCache<'mir, 'tcx>,
borrowed_locals:
RefCell<DataflowResultsRefCursor<'mir, 'tcx, HaveBeenBorrowedLocals<'mir, 'tcx>>>,
}
impl<'mir, 'tcx: 'mir> RequiresStorage<'mir, 'tcx> {
pub fn new(
- body: &'mir Body<'tcx>,
+ body: ReadOnlyBodyCache<'mir, 'tcx>,
borrowed_locals: &'mir DataflowResults<'tcx, HaveBeenBorrowedLocals<'mir, 'tcx>>,
) -> Self {
RequiresStorage {
body,
- borrowed_locals: RefCell::new(DataflowResultsCursor::new(borrowed_locals, body)),
+ borrowed_locals: RefCell::new(
+ DataflowResultsCursor::new(borrowed_locals, body.body())
+ ),
}
}
pub fn body(&self) -> &Body<'tcx> {
- self.body
+ &self.body
}
}
).ok_or_else(|| err_inval!(TooGeneric))?;
let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
- self.write_scalar(Scalar::Ptr(fn_ptr.into()), dest)?;
+ self.write_scalar(fn_ptr, dest)?;
}
_ => bug!("reify fn pointer on {:?}", src.layout.ty),
}
ty::ClosureKind::FnOnce,
);
let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
- let val = Immediate::Scalar(Scalar::Ptr(fn_ptr.into()).into());
- self.write_immediate(val, dest)?;
+ self.write_scalar(fn_ptr, dest)?;
}
_ => bug!("closure fn pointer on {:?}", src.layout.ty),
}
}
}
+impl<'mir, 'tcx, Tag, Extra> Frame<'mir, 'tcx, Tag, Extra> {
+ /// Return the `SourceInfo` of the current instruction.
+ pub fn current_source_info(&self) -> Option<mir::SourceInfo> {
+ self.block.map(|block| {
+ let block = &self.body.basic_blocks()[block];
+ if self.stmt < block.statements.len() {
+ block.statements[self.stmt].source_info
+ } else {
+ block.terminator().source_info
+ }
+ })
+ }
+}
+
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> {
#[inline]
fn data_layout(&self) -> &layout::TargetDataLayout {
self.memory.force_bits(scalar, size)
}
+ /// Call this to turn untagged "global" pointers (obtained via `tcx`) into
+ /// the *canonical* machine pointer to the allocation. Must never be used
+ /// for any other pointers!
+ ///
+ /// This represents a *direct* access to that memory, as opposed to access
+ /// through a pointer that was created by the program.
#[inline(always)]
pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
self.memory.tag_static_base_pointer(ptr)
&self,
instance: ty::InstanceDef<'tcx>,
promoted: Option<mir::Promoted>,
- ) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> {
+ ) -> InterpResult<'tcx, mir::ReadOnlyBodyCache<'tcx, 'tcx>> {
// do not continue if typeck errors occurred (can only occur in local crate)
let did = instance.def_id();
if did.is_local()
}
trace!("load mir(instance={:?}, promoted={:?})", instance, promoted);
if let Some(promoted) = promoted {
- return Ok(&self.tcx.promoted_mir(did)[promoted]);
+ return Ok(self.tcx.promoted_mir(did)[promoted].unwrap_read_only());
}
match instance {
ty::InstanceDef::Item(def_id) => if self.tcx.is_mir_available(did) {
- Ok(self.tcx.optimized_mir(did))
+ Ok(self.tcx.optimized_mir(did).unwrap_read_only())
} else {
throw_unsup!(NoMirFor(self.tcx.def_path_str(def_id)))
},
pub fn generate_stacktrace(&self, explicit_span: Option<Span>) -> Vec<FrameInfo<'tcx>> {
let mut last_span = None;
let mut frames = Vec::new();
- for &Frame { instance, span, body, block, stmt, .. } in self.stack().iter().rev() {
+ for frame in self.stack().iter().rev() {
// make sure we don't emit frames that are duplicates of the previous
- if explicit_span == Some(span) {
- last_span = Some(span);
+ if explicit_span == Some(frame.span) {
+ last_span = Some(frame.span);
continue;
}
if let Some(last) = last_span {
- if last == span {
+ if last == frame.span {
continue;
}
} else {
- last_span = Some(span);
+ last_span = Some(frame.span);
}
- let lint_root = block.and_then(|block| {
- let block = &body.basic_blocks()[block];
- let source_info = if stmt < block.statements.len() {
- block.statements[stmt].source_info
- } else {
- block.terminator().source_info
- };
- match body.source_scope_local_data {
- mir::ClearCrossCrate::Set(ref ivs) => Some(ivs[source_info.scope].lint_root),
+ let lint_root = frame.current_source_info().and_then(|source_info| {
+ match &frame.body.source_scopes[source_info.scope].local_data {
+ mir::ClearCrossCrate::Set(data) => Some(data.lint_root),
mir::ClearCrossCrate::Clear => None,
}
});
- frames.push(FrameInfo { call_site: span, instance, lint_root });
+ frames.push(FrameInfo { call_site: frame.span, instance: frame.instance, lint_root });
}
trace!("generate stacktrace: {:#?}, {:?}", frames, explicit_span);
frames
match intrinsic_name {
"caller_location" => {
- let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
- let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
- let location = self.alloc_caller_location(
- Symbol::intern(&caller.file.name.to_string()),
- caller.line as u32,
- caller.col_display as u32 + 1,
- )?;
+ let location = self.alloc_caller_location_for_span(span);
self.write_scalar(location.ptr, dest)?;
}
if self.binary_op(BinOp::Rem, a, b)?.to_bits()? != 0 {
// Then, check if `b` is -1, which is the "min_value / -1" case.
let minus1 = Scalar::from_int(-1, dest.layout.size);
- let b = b.to_scalar().unwrap();
- if b == minus1 {
+ let b_scalar = b.to_scalar().unwrap();
+ if b_scalar == minus1 {
throw_ub_format!("exact_div: result of dividing MIN by -1 cannot be represented")
} else {
throw_ub_format!(
"exact_div: {} cannot be divided by {} without remainder",
- a.to_scalar().unwrap(),
+ a,
b,
)
}
use rustc::middle::lang_items::PanicLocationLangItem;
-use rustc::mir::interpret::{Pointer, PointerArithmetic, Scalar};
use rustc::ty::subst::Subst;
-use rustc_target::abi::{LayoutOf, Size};
-use syntax_pos::Symbol;
+use rustc_target::abi::LayoutOf;
+use syntax_pos::{Symbol, Span};
-use crate::interpret::{MemoryKind, MPlaceTy, intrinsics::{InterpCx, InterpResult, Machine}};
+use crate::interpret::{Scalar, MemoryKind, MPlaceTy, intrinsics::{InterpCx, Machine}};
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
- pub fn alloc_caller_location(
+ crate fn alloc_caller_location(
&mut self,
filename: Symbol,
line: u32,
col: u32,
- ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+ ) -> MPlaceTy<'tcx, M::PointerTag> {
+ let file = self.allocate_str(&filename.as_str(), MemoryKind::CallerLocation);
let line = Scalar::from_u32(line);
let col = Scalar::from_u32(col);
- let ptr_size = self.pointer_size();
- let u32_size = Size::from_bits(32);
-
+ // Allocate memory for `CallerLocation` struct.
let loc_ty = self.tcx.type_of(self.tcx.require_lang_item(PanicLocationLangItem, None))
.subst(*self.tcx, self.tcx.mk_substs([self.tcx.lifetimes.re_static.into()].iter()));
- let loc_layout = self.layout_of(loc_ty)?;
-
- let file_alloc = self.tcx.allocate_bytes(filename.as_str().as_bytes());
- let file_ptr = Pointer::new(file_alloc, Size::ZERO);
- let file = Scalar::Ptr(self.tag_static_base_pointer(file_ptr));
- let file_len = Scalar::from_uint(filename.as_str().len() as u128, ptr_size);
-
+ let loc_layout = self.layout_of(loc_ty).unwrap();
let location = self.allocate(loc_layout, MemoryKind::CallerLocation);
- let file_out = self.mplace_field(location, 0)?;
- let file_ptr_out = self.force_ptr(self.mplace_field(file_out, 0)?.ptr)?;
- let file_len_out = self.force_ptr(self.mplace_field(file_out, 1)?.ptr)?;
- let line_out = self.force_ptr(self.mplace_field(location, 1)?.ptr)?;
- let col_out = self.force_ptr(self.mplace_field(location, 2)?.ptr)?;
+ // Initialize fields.
+ self.write_immediate(file.to_ref(), self.mplace_field(location, 0).unwrap().into())
+ .expect("writing to memory we just allocated cannot fail");
+ self.write_scalar(line, self.mplace_field(location, 1).unwrap().into())
+ .expect("writing to memory we just allocated cannot fail");
+ self.write_scalar(col, self.mplace_field(location, 2).unwrap().into())
+ .expect("writing to memory we just allocated cannot fail");
- let layout = &self.tcx.data_layout;
- // We just allocated this, so we can skip the bounds checks.
- let alloc = self.memory.get_raw_mut(file_ptr_out.alloc_id)?;
-
- alloc.write_scalar(layout, file_ptr_out, file.into(), ptr_size)?;
- alloc.write_scalar(layout, file_len_out, file_len.into(), ptr_size)?;
- alloc.write_scalar(layout, line_out, line.into(), u32_size)?;
- alloc.write_scalar(layout, col_out, col.into(), u32_size)?;
+ location
+ }
- Ok(location)
+ pub fn alloc_caller_location_for_span(
+ &mut self,
+ span: Span,
+ ) -> MPlaceTy<'tcx, M::PointerTag> {
+ let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
+ let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
+ self.alloc_caller_location(
+ Symbol::intern(&caller.file.name.to_string()),
+ caller.line as u32,
+ caller.col_display as u32 + 1,
+ )
}
}
use syntax_pos::Span;
use super::{
- Allocation, AllocId, InterpResult, Scalar, AllocationExtra,
+ Allocation, AllocId, InterpResult, Scalar, AllocationExtra, AssertMessage,
InterpCx, PlaceTy, OpTy, ImmTy, MemoryKind, Pointer, Memory,
Frame, Operand,
};
unwind: Option<mir::BasicBlock>,
) -> InterpResult<'tcx>;
+ /// Called to evaluate `Assert` MIR terminators that trigger a panic.
+ fn assert_panic(
+ ecx: &mut InterpCx<'mir, 'tcx, Self>,
+ span: Span,
+ msg: &AssertMessage<'tcx>,
+ unwind: Option<mir::BasicBlock>,
+ ) -> InterpResult<'tcx>;
+
/// Called for read access to a foreign static item.
///
/// This will only be called once per static and machine; the result is cached in
/// cache the result. (This relies on `AllocMap::get_or` being able to add the
/// owned allocation to the map even when the map is shared.)
///
- /// For static allocations, the tag returned must be the same as the one returned by
- /// `tag_static_base_pointer`.
- fn tag_allocation<'b>(
+ /// Also return the "base" tag to use for this allocation: the one that is used for direct
+ /// accesses to this allocation. If `kind == STATIC_KIND`, this tag must be consistent
+ /// with `tag_static_base_pointer`.
+ fn init_allocation_extra<'b>(
memory_extra: &Self::MemoryExtra,
id: AllocId,
alloc: Cow<'b, Allocation>,
kind: Option<MemoryKind<Self::MemoryKinds>>,
) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag);
- /// Return the "base" tag for the given static allocation: the one that is used for direct
- /// accesses to this static/const/fn allocation.
- ///
- /// Be aware that requesting the `Allocation` for that `id` will lead to cycles
- /// for cyclic statics!
+ /// Return the "base" tag for the given *static* allocation: the one that is used for direct
+ /// accesses to this static/const/fn allocation. If `id` is not a static allocation,
+ /// this will return an unusable tag (i.e., accesses will be UB)!
fn tag_static_base_pointer(
memory_extra: &Self::MemoryExtra,
id: AllocId,
}
}
+ /// Call this to turn untagged "global" pointers (obtained via `tcx`) into
+ /// the *canonical* machine pointer to the allocation. Must never be used
+ /// for any other pointers!
+ ///
+ /// This represents a *direct* access to that memory, as opposed to access
+ /// through a pointer that was created by the program.
#[inline]
pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
ptr.with_tag(M::tag_static_base_pointer(&self.extra, ptr.alloc_id))
kind: MemoryKind<M::MemoryKinds>,
) -> Pointer<M::PointerTag> {
let id = self.tcx.alloc_map.lock().reserve();
- let (alloc, tag) = M::tag_allocation(&self.extra, id, Cow::Owned(alloc), Some(kind));
+ debug_assert_ne!(Some(kind), M::STATIC_KIND.map(MemoryKind::Machine),
+ "dynamically allocating static memory");
+ let (alloc, tag) = M::init_allocation_extra(&self.extra, id, Cow::Owned(alloc), Some(kind));
self.alloc_map.insert(id, (kind, alloc.into_owned()));
Pointer::from(id).with_tag(tag)
}
sptr
} else {
// A "real" access, we must get a pointer.
- Scalar::Ptr(self.force_ptr(sptr)?)
+ Scalar::from(self.force_ptr(sptr)?)
};
Ok(match normalized.to_bits_or_ptr(self.pointer_size(), self) {
Ok(bits) => {
}
}
};
- // We got tcx memory. Let the machine figure out whether and how to
- // turn that into memory with the right pointer tag.
- Ok(M::tag_allocation(
+ // We got tcx memory. Let the machine initialize its "extra" stuff.
+ let (alloc, tag) = M::init_allocation_extra(
memory_extra,
id, // always use the ID we got as input, not the "hidden" one.
alloc,
M::STATIC_KIND.map(MemoryKind::Machine),
- ).0)
+ );
+ debug_assert_eq!(tag, M::tag_static_base_pointer(memory_extra, id));
+ Ok(alloc)
}
/// Gives raw access to the `Allocation`, without bounds or alignment checks.
};
pub use rustc::mir::interpret::ScalarMaybeUndef;
use rustc_macros::HashStable;
+use syntax::ast;
/// An `Immediate` represents a single immediate self-contained Rust value.
///
/// For optimization of a few very common cases, there is also a representation for a pair of
/// primitive values (`ScalarPair`). It allows Miri to avoid making allocations for checked binary
-/// operations and fat pointers. This idea was taken from rustc's codegen.
+/// operations and wide pointers. This idea was taken from rustc's codegen.
/// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely
/// defined on `Immediate`, and do not have to work with a `Place`.
#[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, Hash)]
}
}
+impl<Tag> From<Pointer<Tag>> for Immediate<Tag> {
+ #[inline(always)]
+ fn from(val: Pointer<Tag>) -> Self {
+ Immediate::Scalar(Scalar::from(val).into())
+ }
+}
+
impl<'tcx, Tag> Immediate<Tag> {
pub fn new_slice(
val: Scalar<Tag>,
}
pub fn new_dyn_trait(val: Scalar<Tag>, vtable: Pointer<Tag>) -> Self {
- Immediate::ScalarPair(val.into(), Scalar::Ptr(vtable).into())
+ Immediate::ScalarPair(val.into(), vtable.into())
}
#[inline]
pub fn to_scalar_or_undef(self) -> ScalarMaybeUndef<Tag> {
match self {
Immediate::Scalar(val) => val,
- Immediate::ScalarPair(..) => bug!("Got a fat pointer where a scalar was expected"),
+ Immediate::ScalarPair(..) => bug!("Got a wide pointer where a scalar was expected"),
}
}
pub layout: TyLayout<'tcx>,
}
+// `Tag: Copy` because some methods on `Scalar` consume them by value
+impl<Tag: Copy> std::fmt::Display for ImmTy<'tcx, Tag> {
+ fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match &self.imm {
+ Immediate::Scalar(ScalarMaybeUndef::Scalar(s)) => match s.to_bits(self.layout.size) {
+ Ok(s) => {
+ match self.layout.ty.kind {
+ ty::Int(_) => return write!(
+ fmt, "{}",
+ super::sign_extend(s, self.layout.size) as i128,
+ ),
+ ty::Uint(_) => return write!(fmt, "{}", s),
+ ty::Bool if s == 0 => return fmt.write_str("false"),
+ ty::Bool if s == 1 => return fmt.write_str("true"),
+ ty::Char => if let Some(c) =
+ u32::try_from(s).ok().and_then(std::char::from_u32) {
+ return write!(fmt, "{}", c);
+ },
+ ty::Float(ast::FloatTy::F32) => if let Ok(u) = u32::try_from(s) {
+ return write!(fmt, "{}", f32::from_bits(u));
+ },
+ ty::Float(ast::FloatTy::F64) => if let Ok(u) = u64::try_from(s) {
+ return write!(fmt, "{}", f64::from_bits(u));
+ },
+ _ => {},
+ }
+ write!(fmt, "{:x}", s)
+ },
+ Err(_) => fmt.write_str("{pointer}"),
+ },
+ Immediate::Scalar(ScalarMaybeUndef::Undef) => fmt.write_str("{undef}"),
+ Immediate::ScalarPair(..) => fmt.write_str("{wide pointer or tuple}"),
+ }
+ }
+}
+
impl<'tcx, Tag> ::std::ops::Deref for ImmTy<'tcx, Tag> {
type Target = Immediate<Tag>;
#[inline(always)]
Ok(self.read_immediate(op)?.to_scalar_or_undef())
}
- // Turn the MPlace into a string (must already be dereferenced!)
+ // Turn the wide MPlace into a string (must already be dereferenced!)
pub fn read_str(
&self,
mplace: MPlaceTy<'tcx, M::PointerTag>,
Self::from_scalar_ptr(ptr.into(), align)
}
- /// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space.
+ /// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space.
/// This is the inverse of `ref_to_mplace`.
#[inline(always)]
pub fn to_ref(self) -> Immediate<Tag> {
M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<Tag, M::AllocExtra>)>,
M::AllocExtra: AllocationExtra<Tag>,
{
- /// Take a value, which represents a (thin or fat) reference, and make it a place.
+ /// Take a value, which represents a (thin or wide) reference, and make it a place.
/// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`.
///
/// Only call this if you are sure the place is "valid" (aligned and inbounds), or do not
use rustc::mir::PlaceBase;
let mut place_ty = match &place.base {
- PlaceBase::Local(mir::RETURN_PLACE) => match self.frame().return_place {
- Some(return_place) => {
- // We use our layout to verify our assumption; caller will validate
- // their layout on return.
- PlaceTy {
- place: *return_place,
- layout: self.layout_of(
- self.subst_from_frame_and_normalize_erasing_regions(
- self.frame().body.return_ty()
- )
- )?,
- }
+ PlaceBase::Local(mir::RETURN_PLACE) => {
+ // `return_place` has the *caller* layout, but we want to use our
+ // `layout to verify our assumption. The caller will validate
+ // their layout on return.
+ PlaceTy {
+ place: match self.frame().return_place {
+ Some(p) => *p,
+ // Even if we don't have a return place, we sometimes need to
+ // create this place, but any attempt to read from / write to it
+ // (even a ZST read/write) needs to error, so let us make this
+ // a NULL place.
+ //
+ // FIXME: Ideally we'd make sure that the place projections also
+ // bail out.
+ None => Place::null(&*self),
+ },
+ layout: self.layout_of(
+ self.subst_from_frame_and_normalize_erasing_regions(
+ self.frame().body.return_ty()
+ )
+ )?,
}
- None => throw_unsup!(InvalidNullPointerUsage),
},
PlaceBase::Local(local) => PlaceTy {
// This works even for dead/uninitialized locals; we check further when writing
}
/// Write a scalar to a place
+ #[inline(always)]
pub fn write_scalar(
&mut self,
val: impl Into<ScalarMaybeUndef<M::PointerTag>>,
// to handle padding properly, which is only correct if we never look at this data with the
// wrong type.
- let ptr = match self.check_mplace_access(dest, None)
- .expect("places should be checked on creation")
+ // Invalid places are a thing: the return place of a diverging function
+ let ptr = match self.check_mplace_access(dest, None)?
{
Some(ptr) => ptr,
None => return Ok(()), // zero-sized access
MPlaceTy::from_aligned_ptr(ptr, layout)
}
+ /// Returns a wide MPlace.
+ pub fn allocate_str(
+ &mut self,
+ str: &str,
+ kind: MemoryKind<M::MemoryKinds>,
+ ) -> MPlaceTy<'tcx, M::PointerTag> {
+ let ptr = self.memory.allocate_static_bytes(str.as_bytes(), kind);
+ let meta = Scalar::from_uint(str.len() as u128, self.pointer_size());
+ let mplace = MemPlace {
+ ptr: ptr.into(),
+ align: Align::from_bytes(1).unwrap(),
+ meta: Some(meta),
+ };
+
+ let layout = self.layout_of(self.tcx.mk_static_str()).unwrap();
+ MPlaceTy { mplace, layout }
+ }
+
pub fn write_discriminant_index(
&mut self,
variant_index: VariantIdx,
use rustc_target::spec::abi::Abi;
use super::{
- GlobalId, InterpResult, PointerArithmetic,
- InterpCx, Machine, OpTy, ImmTy, PlaceTy, MPlaceTy, StackPopCleanup, FnVal,
+ GlobalId, InterpResult, InterpCx, Machine,
+ OpTy, ImmTy, PlaceTy, MPlaceTy, StackPopCleanup, FnVal,
};
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
expected,
ref msg,
target,
- ..
+ cleanup,
} => {
let cond_val = self.read_immediate(self.eval_operand(cond, None)?)?
.to_scalar()?.to_bool()?;
if expected == cond_val {
self.go_to_block(target);
} else {
- // Compute error message
- use rustc::mir::interpret::PanicInfo::*;
- return Err(match msg {
- BoundsCheck { ref len, ref index } => {
- let len = self
- .read_immediate(self.eval_operand(len, None)?)
- .expect("can't eval len")
- .to_scalar()?
- .to_bits(self.memory.pointer_size())? as u64;
- let index = self
- .read_immediate(self.eval_operand(index, None)?)
- .expect("can't eval index")
- .to_scalar()?
- .to_bits(self.memory.pointer_size())? as u64;
- err_panic!(BoundsCheck { len, index })
- }
- Overflow(op) => err_panic!(Overflow(*op)),
- OverflowNeg => err_panic!(OverflowNeg),
- DivisionByZero => err_panic!(DivisionByZero),
- RemainderByZero => err_panic!(RemainderByZero),
- ResumedAfterReturn(generator_kind)
- => err_panic!(ResumedAfterReturn(*generator_kind)),
- ResumedAfterPanic(generator_kind)
- => err_panic!(ResumedAfterPanic(*generator_kind)),
- Panic { .. } => bug!("`Panic` variant cannot occur in MIR"),
- }
- .into());
+ M::assert_panic(self, terminator.source_info.span, msg, cleanup)?;
}
}
return Ok(())
},
+ // It is UB to ever encounter this.
+ Unreachable => throw_ub!(Unreachable),
+
+ // These should never occur for MIR we actually run.
+ DropAndReplace { .. } |
+ FalseEdges { .. } |
+ FalseUnwind { .. } =>
+ bug!("{:#?} should have been eliminated by MIR pass", terminator.kind),
+
+ // These are not (yet) supported. It is unclear if they even can occur in
+ // MIR that we actually run.
Yield { .. } |
GeneratorDrop |
- DropAndReplace { .. } |
- Abort => unimplemented!("{:#?}", terminator.kind),
- FalseEdges { .. } => bug!("should have been eliminated by\
- `simplify_branches` mir pass"),
- FalseUnwind { .. } => bug!("should have been eliminated by\
- `simplify_branches` mir pass"),
- Unreachable => throw_ub!(Unreachable),
+ Abort =>
+ throw_unsup_format!("Unsupported terminator kind: {:#?}", terminator.kind),
}
Ok(())
// allocation is correctly aligned as we created it above. Also we're only offsetting by
// multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`.
let vtable_alloc = self.memory.get_raw_mut(vtable.alloc_id)?;
- vtable_alloc.write_ptr_sized(tcx, vtable, Scalar::Ptr(drop).into())?;
+ vtable_alloc.write_ptr_sized(tcx, vtable, drop.into())?;
let size_ptr = vtable.offset(ptr_size, tcx)?;
vtable_alloc.write_ptr_sized(tcx, size_ptr, Scalar::from_uint(size, ptr_size).into())?;
// We cannot use `vtable_allic` as we are creating fn ptrs in this loop.
let method_ptr = vtable.offset(ptr_size * (3 + i as u64), tcx)?;
self.memory.get_raw_mut(vtable.alloc_id)?
- .write_ptr_sized(tcx, method_ptr, Scalar::Ptr(fn_ptr).into())?;
+ .write_ptr_sized(tcx, method_ptr, fn_ptr.into())?;
}
}
#![feature(stmt_expr_attributes)]
#![feature(bool_to_option)]
#![feature(trait_alias)]
+#![feature(matches_macro)]
#![recursion_limit="256"]
body: &body,
output,
param_substs: instance.substs,
- }.visit_body(&body);
+ }.visit_body(body);
}
fn def_id_to_string(tcx: TyCtxt<'_>, def_id: DefId) -> String {
providers.mir_shims = make_shim;
}
-fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx Body<'tcx> {
+fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx BodyCache<'tcx> {
debug!("make_shim({:?})", instance);
let mut result = match instance {
debug!("make_shim({:?}) = {:?}", instance, result);
+ result.ensure_predecessors();
tcx.arena.alloc(result)
}
.collect()
}
-fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>) -> Body<'tcx> {
+fn build_drop_shim<'tcx>(
+ tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>
+) -> BodyCache<'tcx> {
debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty);
// Check if this is a generator, if so, return the drop glue for it
block(&mut blocks, TerminatorKind::Goto { target: return_block });
block(&mut blocks, TerminatorKind::Return);
- let mut body = new_body(
+ let body = new_body(
blocks,
- IndexVec::from_elem_n(
- SourceScopeData { span, parent_scope: None }, 1
- ),
local_decls_for_sig(&sig, span),
sig.inputs().len(),
span);
+ let mut body = BodyCache::new(body);
+
if let Some(..) = ty {
// The first argument (index 0), but add 1 for the return value.
let dropee_ptr = Place::from(Local::new(1+0));
fn new_body<'tcx>(
basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
- source_scopes: IndexVec<SourceScope, SourceScopeData>,
local_decls: IndexVec<Local, LocalDecl<'tcx>>,
arg_count: usize,
span: Span,
) -> Body<'tcx> {
Body::new(
basic_blocks,
- source_scopes,
- ClearCrossCrate::Clear,
+ IndexVec::from_elem_n(
+ SourceScopeData { span, parent_scope: None, local_data: ClearCrossCrate::Clear },
+ 1,
+ ),
local_decls,
IndexVec::new(),
arg_count,
}
/// Builds a `Clone::clone` shim for `self_ty`. Here, `def_id` is `Clone::clone`.
-fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Body<'tcx> {
+fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> BodyCache<'tcx> {
debug!("build_clone_shim(def_id={:?})", def_id);
let param_env = tcx.param_env(def_id);
}
};
- builder.into_mir()
+ BodyCache::new(builder.into_mir())
}
struct CloneShimBuilder<'tcx> {
fn into_mir(self) -> Body<'tcx> {
new_body(
self.blocks,
- IndexVec::from_elem_n(
- SourceScopeData { span: self.span, parent_scope: None }, 1
- ),
self.local_decls,
self.sig.inputs().len(),
self.span,
rcvr_adjustment: Adjustment,
call_kind: CallKind,
untuple_args: Option<&[Ty<'tcx>]>,
-) -> Body<'tcx> {
+) -> BodyCache<'tcx> {
debug!("build_call_shim(def_id={:?}, rcvr_adjustment={:?}, \
call_kind={:?}, untuple_args={:?})",
def_id, rcvr_adjustment, call_kind, untuple_args);
let mut body = new_body(
blocks,
- IndexVec::from_elem_n(
- SourceScopeData { span, parent_scope: None }, 1
- ),
local_decls,
sig.inputs().len(),
span,
if let Abi::RustCall = sig.abi {
body.spread_arg = Some(Local::new(sig.inputs().len()));
}
- body
+ BodyCache::new(body)
}
-pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> &Body<'_> {
+pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> &BodyCache<'_> {
debug_assert!(tcx.is_constructor(ctor_id));
let span = tcx.hir().span_if_local(ctor_id)
let body = new_body(
IndexVec::from_elem_n(start_block, 1),
- IndexVec::from_elem_n(
- SourceScopeData { span, parent_scope: None }, 1
- ),
local_decls,
sig.inputs().len(),
span,
|_, _| Ok(()),
);
+ let mut body = BodyCache::new(body);
+ body.ensure_predecessors();
tcx.arena.alloc(body)
}
*/
impl<'tcx> MirPass<'tcx> for AddCallGuards {
- fn run_pass(&self, _tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(
+ &self, _tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut BodyCache<'tcx>
+ ) {
self.add_call_guards(body);
}
}
impl AddCallGuards {
- pub fn add_call_guards(&self, body: &mut Body<'_>) {
- let pred_count: IndexVec<_, _> =
- body.predecessors().iter().map(|ps| ps.len()).collect();
+ pub fn add_call_guards(&self, body: &mut BodyCache<'_>) {
+ let pred_count: IndexVec<_, _> = body.predecessors().iter().map(|ps| ps.len()).collect();
// We need a place to store the new blocks generated
let mut new_blocks = Vec::new();
pub struct AddMovesForPackedDrops;
impl<'tcx> MirPass<'tcx> for AddMovesForPackedDrops {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyCache<'tcx>) {
debug!("add_moves_for_packed_drops({:?} @ {:?})", src, body.span);
add_moves_for_packed_drops(tcx, body, src.def_id());
}
}
-pub fn add_moves_for_packed_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, def_id: DefId) {
+pub fn add_moves_for_packed_drops<'tcx>(
+ tcx: TyCtxt<'tcx>, body: &mut BodyCache<'tcx>, def_id: DefId
+) {
let patch = add_moves_for_packed_drops_patch(tcx, body, def_id);
patch.apply(body);
}
}
impl<'tcx> MirPass<'tcx> for AddRetag {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut BodyCache<'tcx>) {
if !tcx.sess.opts.debugging_opts.mir_emit_retag {
return;
}
/// Information about the item currently being const-checked, as well as a reference to the global
/// context.
pub struct Item<'mir, 'tcx> {
- pub body: &'mir mir::Body<'tcx>,
+ pub body: mir::ReadOnlyBodyCache<'mir, 'tcx>,
pub tcx: TyCtxt<'tcx>,
pub def_id: DefId,
pub param_env: ty::ParamEnv<'tcx>,
pub fn new(
tcx: TyCtxt<'tcx>,
def_id: DefId,
- body: &'mir mir::Body<'tcx>,
+ body: mir::ReadOnlyBodyCache<'mir, 'tcx>,
) -> Self {
let param_env = tcx.param_env(def_id);
let const_kind = ConstKind::for_item(tcx, def_id);
use rustc::hir::def_id::DefId;
use syntax_pos::DUMMY_SP;
-use super::{ConstKind, Item as ConstCx};
+use super::Item as ConstCx;
pub fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> ConstQualifs {
ConstQualifs {
/// of the type.
fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> bool;
- fn in_static(_cx: &ConstCx<'_, 'tcx>, _def_id: DefId) -> bool {
- // FIXME(eddyb) should we do anything here for value properties?
- false
+ fn in_static(cx: &ConstCx<'_, 'tcx>, def_id: DefId) -> bool {
+ // `mir_const_qualif` does return the qualifs in the final value of a `static`, so we could
+ // use value-based qualification here, but we shouldn't do this without a good reason.
+ Self::in_any_value_of_ty(cx, cx.tcx.type_of(def_id))
}
fn in_projection_structurally(
});
let qualif = base_qualif && Self::in_any_value_of_ty(
cx,
- Place::ty_from(place.base, proj_base, cx.body, cx.tcx)
+ Place::ty_from(place.base, proj_base, &*cx.body, cx.tcx)
.projection_ty(cx.tcx, elem)
.ty,
);
// Special-case reborrows to be more like a copy of the reference.
if let &[ref proj_base @ .., elem] = place.projection.as_ref() {
if ProjectionElem::Deref == elem {
- let base_ty = Place::ty_from(&place.base, proj_base, cx.body, cx.tcx).ty;
+ let base_ty = Place::ty_from(&place.base, proj_base, &*cx.body, cx.tcx).ty;
if let ty::Ref(..) = base_ty.kind {
return Self::in_place(cx, per_local, PlaceRef {
base: &place.base,
rvalue: &Rvalue<'tcx>,
) -> bool {
match *rvalue {
- // Returning `true` for `Rvalue::Ref` indicates the borrow isn't
- // allowed in constants (and the `Checker` will error), and/or it
- // won't be promoted, due to `&mut ...` or interior mutability.
- Rvalue::Ref(_, kind, ref place) => {
- let ty = place.ty(cx.body, cx.tcx).ty;
-
- if let BorrowKind::Mut { .. } = kind {
- // In theory, any zero-sized value could be borrowed
- // mutably without consequences.
- match ty.kind {
- // Inside a `static mut`, &mut [...] is also allowed.
- | ty::Array(..)
- | ty::Slice(_)
- if cx.const_kind == Some(ConstKind::StaticMut)
- => {},
-
- // FIXME(eddyb): We only return false for `&mut []` outside a const
- // context which seems unnecessary given that this is merely a ZST.
- | ty::Array(_, len)
- if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0)
- && cx.const_kind == None
- => {},
-
- _ => return true,
- }
- }
- }
-
Rvalue::Aggregate(ref kind, _) => {
if let AggregateKind::Adt(def, ..) = **kind {
if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
- let ty = rvalue.ty(cx.body, cx.tcx);
+ let ty = rvalue.ty(&*cx.body, cx.tcx);
assert_eq!(Self::in_any_value_of_ty(cx, ty), true);
return true;
}
args: &[mir::Operand<'tcx>],
return_place: &mir::Place<'tcx>,
) {
- let return_ty = return_place.ty(self.item.body, self.item.tcx).ty;
+ let return_ty = return_place.ty(&*self.item.body, self.item.tcx).ty;
let qualif = Q::in_call(
self.item,
&|l| self.qualifs_per_local.contains(l),
use super::resolver::FlowSensitiveAnalysis;
use super::{ConstKind, Item, Qualif, is_lang_panic_fn};
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum CheckOpResult {
- Forbidden,
- Unleashed,
- Allowed,
-}
-
pub type IndirectlyMutableResults<'mir, 'tcx> =
old_dataflow::DataflowResultsCursor<'mir, 'tcx, IndirectlyMutableLocals<'mir, 'tcx>>;
) -> Self {
let analysis = FlowSensitiveAnalysis::new(q, item);
let results =
- dataflow::Engine::new(item.tcx, item.body, item.def_id, dead_unwinds, analysis)
+ dataflow::Engine::new(item.tcx, &item.body, item.def_id, dead_unwinds, analysis)
.iterate_to_fixpoint();
- let cursor = dataflow::ResultsCursor::new(item.body, results);
+ let cursor = dataflow::ResultsCursor::new(item.body.body(), results);
let mut in_any_value_of_ty = BitSet::new_empty(item.body.local_decls.len());
for (local, decl) in item.body.local_decls.iter_enumerated() {
/// The span of the current statement.
span: Span,
-
- /// True if the local was assigned the result of an illegal borrow (`ops::MutBorrow`).
- ///
- /// This is used to hide errors from {re,}borrowing the newly-assigned local, instead pointing
- /// the user to the place where the illegal borrow occurred. This set is only populated once an
- /// error has been emitted, so it will never cause an erroneous `mir::Body` to pass validation.
- ///
- /// FIXME(ecstaticmorse): assert at the end of checking that if `tcx.has_errors() == false`,
- /// this set is empty. Note that if we start removing locals from
- /// `derived_from_illegal_borrow`, just checking at the end won't be enough.
- derived_from_illegal_borrow: BitSet<Local>,
}
impl Deref for Validator<'_, 'mir, 'tcx> {
let indirectly_mutable = old_dataflow::do_dataflow(
item.tcx,
- item.body,
+ &*item.body,
item.def_id,
&item.tcx.get_attrs(item.def_id),
&dead_unwinds,
- old_dataflow::IndirectlyMutableLocals::new(item.tcx, item.body, item.param_env),
+ old_dataflow::IndirectlyMutableLocals::new(item.tcx, item.body.body(), item.param_env),
|_, local| old_dataflow::DebugFormatted::new(&local),
);
let indirectly_mutable = old_dataflow::DataflowResultsCursor::new(
indirectly_mutable,
- item.body,
+ item.body.body(),
);
let qualifs = Qualifs {
span: item.body.span,
item,
qualifs,
- derived_from_illegal_borrow: BitSet::new_empty(item.body.local_decls.len()),
}
}
if use_min_const_fn_checks {
// Enforce `min_const_fn` for stable `const fn`s.
use crate::transform::qualify_min_const_fn::is_min_const_fn;
- if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
+ if let Err((span, err)) = is_min_const_fn(tcx, def_id, &body) {
error_min_const_fn_violation(tcx, span, err);
return;
}
if should_check_for_sync {
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
- check_return_ty_is_sync(tcx, body, hir_id);
+ check_return_ty_is_sync(tcx, &body, hir_id);
}
}
}
/// Emits an error at the given `span` if an expression cannot be evaluated in the current
- /// context. Returns `Forbidden` if an error was emitted.
- pub fn check_op_spanned<O>(&mut self, op: O, span: Span) -> CheckOpResult
+ /// context.
+ pub fn check_op_spanned<O>(&mut self, op: O, span: Span)
where
O: NonConstOp
{
trace!("check_op: op={:?}", op);
if op.is_allowed_in_item(self) {
- return CheckOpResult::Allowed;
+ return;
}
// If an operation is supported in miri (and is not already controlled by a feature gate) it
if is_unleashable && self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
self.tcx.sess.span_warn(span, "skipping const checks");
- return CheckOpResult::Unleashed;
+ return;
}
op.emit_error(self, span);
- CheckOpResult::Forbidden
}
/// Emits an error if an expression cannot be evaluated in the current context.
- pub fn check_op(&mut self, op: impl NonConstOp) -> CheckOpResult {
+ pub fn check_op(&mut self, op: impl NonConstOp) {
let span = self.span;
self.check_op_spanned(op, span)
}
- fn check_static(&mut self, def_id: DefId, span: Span) -> CheckOpResult {
+ fn check_static(&mut self, def_id: DefId, span: Span) {
let is_thread_local = self.tcx.has_attr(def_id, sym::thread_local);
if is_thread_local {
self.check_op_spanned(ops::ThreadLocalAccess, span)
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
- // Check nested operands and places.
+ // Special-case reborrows to be more like a copy of a reference.
if let Rvalue::Ref(_, kind, ref place) = *rvalue {
- // Special-case reborrows to be more like a copy of a reference.
- let mut reborrow_place = None;
- if let &[ref proj_base @ .., elem] = place.projection.as_ref() {
- if elem == ProjectionElem::Deref {
- let base_ty = Place::ty_from(&place.base, proj_base, self.body, self.tcx).ty;
- if let ty::Ref(..) = base_ty.kind {
- reborrow_place = Some(proj_base);
- }
- }
- }
-
- if let Some(proj) = reborrow_place {
+ if let Some(reborrowed_proj) = place_as_reborrow(self.tcx, &*self.body, place) {
let ctx = match kind {
BorrowKind::Shared => PlaceContext::NonMutatingUse(
NonMutatingUseContext::SharedBorrow,
),
};
self.visit_place_base(&place.base, ctx, location);
- self.visit_projection(&place.base, proj, ctx, location);
- } else {
- self.super_rvalue(rvalue, location);
+ self.visit_projection(&place.base, reborrowed_proj, ctx, location);
+ return;
}
- } else {
- self.super_rvalue(rvalue, location);
}
+ self.super_rvalue(rvalue, location);
+
match *rvalue {
Rvalue::Use(_) |
Rvalue::Repeat(..) |
Rvalue::Cast(CastKind::Pointer(_), ..) |
Rvalue::Discriminant(..) |
Rvalue::Len(_) |
- Rvalue::Ref(..) |
Rvalue::Aggregate(..) => {}
+ | Rvalue::Ref(_, kind @ BorrowKind::Mut { .. }, ref place)
+ | Rvalue::Ref(_, kind @ BorrowKind::Unique, ref place)
+ => {
+ let ty = place.ty(&*self.body, self.tcx).ty;
+ let is_allowed = match ty.kind {
+ // Inside a `static mut`, `&mut [...]` is allowed.
+ ty::Array(..) | ty::Slice(_) if self.const_kind() == ConstKind::StaticMut
+ => true,
+
+ // FIXME(ecstaticmorse): We could allow `&mut []` inside a const context given
+ // that this is merely a ZST and it is already eligible for promotion.
+ // This may require an RFC?
+ /*
+ ty::Array(_, len) if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0)
+ => true,
+ */
+
+ _ => false,
+ };
+
+ if !is_allowed {
+ self.check_op(ops::MutBorrow(kind));
+ }
+ }
+
+ // At the moment, `PlaceBase::Static` is only used for promoted MIR.
+ | Rvalue::Ref(_, BorrowKind::Shared, ref place)
+ | Rvalue::Ref(_, BorrowKind::Shallow, ref place)
+ if matches!(place.base, PlaceBase::Static(_))
+ => bug!("Saw a promoted during const-checking, which must run before promotion"),
+
+ | Rvalue::Ref(_, kind @ BorrowKind::Shared, ref place)
+ | Rvalue::Ref(_, kind @ BorrowKind::Shallow, ref place)
+ => {
+ // FIXME: Change the `in_*` methods to take a `FnMut` so we don't have to manually
+ // seek the cursors beforehand.
+ self.qualifs.has_mut_interior.cursor.seek_before(location);
+ self.qualifs.indirectly_mutable.seek(location);
+
+ let borrowed_place_has_mut_interior = HasMutInterior::in_place(
+ &self.item,
+ &|local| self.qualifs.has_mut_interior_eager_seek(local),
+ place.as_ref(),
+ );
+
+ if borrowed_place_has_mut_interior {
+ self.check_op(ops::MutBorrow(kind));
+ }
+ }
+
Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
- let operand_ty = operand.ty(self.body, self.tcx);
+ let operand_ty = operand.ty(&*self.body, self.tcx);
let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
}
Rvalue::BinaryOp(op, ref lhs, _) => {
- if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind {
+ if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(&*self.body, self.tcx).kind {
assert!(op == BinOp::Eq || op == BinOp::Ne ||
op == BinOp::Le || op == BinOp::Lt ||
op == BinOp::Ge || op == BinOp::Gt ||
}
}
- fn visit_assign(&mut self, dest: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
- trace!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
-
- // Error on mutable borrows or shared borrows of values with interior mutability.
- //
- // This replicates the logic at the start of `assign` in the old const checker. Note that
- // it depends on `HasMutInterior` being set for mutable borrows as well as values with
- // interior mutability.
- if let Rvalue::Ref(_, kind, ref borrowed_place) = *rvalue {
- // FIXME: Change the `in_*` methods to take a `FnMut` so we don't have to manually seek
- // the cursors beforehand.
- self.qualifs.has_mut_interior.cursor.seek_before(location);
- self.qualifs.indirectly_mutable.seek(location);
-
- let rvalue_has_mut_interior = HasMutInterior::in_rvalue(
- &self.item,
- &|local| self.qualifs.has_mut_interior_eager_seek(local),
- rvalue,
- );
-
- if rvalue_has_mut_interior {
- let is_derived_from_illegal_borrow = match borrowed_place.as_local() {
- // If an unprojected local was borrowed and its value was the result of an
- // illegal borrow, suppress this error and mark the result of this borrow as
- // illegal as well.
- Some(borrowed_local)
- if self.derived_from_illegal_borrow.contains(borrowed_local) =>
- {
- true
- }
-
- // Otherwise proceed normally: check the legality of a mutable borrow in this
- // context.
- _ => self.check_op(ops::MutBorrow(kind)) == CheckOpResult::Forbidden,
- };
-
- // When the target of the assignment is a local with no projections, mark it as
- // derived from an illegal borrow if necessary.
- //
- // FIXME: should we also clear `derived_from_illegal_borrow` when a local is
- // assigned a new value?
- if is_derived_from_illegal_borrow {
- if let Some(dest) = dest.as_local() {
- self.derived_from_illegal_borrow.insert(dest);
- }
- }
- }
- }
-
- self.super_assign(dest, rvalue, location);
- }
-
fn visit_projection_elem(
&mut self,
place_base: &PlaceBase<'tcx>,
match elem {
ProjectionElem::Deref => {
- let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
+ let base_ty = Place::ty_from(place_base, proj_base, &*self.body, self.tcx).ty;
if let ty::RawPtr(_) = base_ty.kind {
if proj_base.is_empty() {
if let (PlaceBase::Local(local), []) = (place_base, proj_base) {
ProjectionElem::Subslice {..} |
ProjectionElem::Field(..) |
ProjectionElem::Index(_) => {
- let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
+ let base_ty = Place::ty_from(place_base, proj_base, &*self.body, self.tcx).ty;
match base_ty.ty_adt_def() {
Some(def) if def.is_union() => {
self.check_op(ops::UnionAccess);
match kind {
TerminatorKind::Call { func, .. } => {
- let fn_ty = func.ty(self.body, self.tcx);
+ let fn_ty = func.ty(&*self.body, self.tcx);
let def_id = match fn_ty.kind {
ty::FnDef(def_id, _) => def_id,
// Check to see if the type of this place can ever have a drop impl. If not, this
// `Drop` terminator is frivolous.
let ty_needs_drop = dropped_place
- .ty(self.body, self.tcx)
+ .ty(&*self.body, self.tcx)
.ty
.needs_drop(self.tcx, self.param_env);
}
});
}
+
+fn place_as_reborrow(
+ tcx: TyCtxt<'tcx>,
+ body: &Body<'tcx>,
+ place: &'a Place<'tcx>,
+) -> Option<&'a [PlaceElem<'tcx>]> {
+ place
+ .projection
+ .split_last()
+ .and_then(|(outermost, inner)| {
+ if outermost != &ProjectionElem::Deref {
+ return None;
+ }
+
+ // A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const`
+ // that points to the allocation for the static. Don't treat these as reborrows.
+ if let PlaceBase::Local(local) = place.base {
+ if body.local_decls[local].is_ref_to_static() {
+ return None;
+ }
+ }
+
+ // Ensure the type being derefed is a reference and not a raw pointer.
+ //
+ // This is sufficient to prevent an access to a `static mut` from being marked as a
+ // reborrow, even if the check above were to disappear.
+ let inner_ty = Place::ty_from(&place.base, inner, body, tcx).ty;
+ match inner_ty.kind {
+ ty::Ref(..) => Some(inner),
+ _ => None,
+ }
+ })
+}
use rustc_data_structures::fx::FxHashSet;
-use rustc_index::vec::IndexVec;
-use rustc_data_structures::sync::Lrc;
use rustc::ty::query::Providers;
use rustc::ty::{self, TyCtxt};
body: &'a Body<'tcx>,
const_context: bool,
min_const_fn: bool,
- source_scope_local_data: &'a IndexVec<SourceScope, SourceScopeLocalData>,
violations: Vec<UnsafetyViolation>,
source_info: SourceInfo,
tcx: TyCtxt<'tcx>,
const_context: bool,
min_const_fn: bool,
body: &'a Body<'tcx>,
- source_scope_local_data: &'a IndexVec<SourceScope, SourceScopeLocalData>,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Self {
body,
const_context,
min_const_fn,
- source_scope_local_data,
violations: vec![],
source_info: SourceInfo {
span: body.span,
if context.is_borrow() {
if util::is_disaligned(self.tcx, self.body, self.param_env, place) {
let source_info = self.source_info;
- let lint_root =
- self.source_scope_local_data[source_info.scope].lint_root;
+ let lint_root = self.body.source_scopes[source_info.scope]
+ .local_data
+ .as_ref()
+ .assert_crate_local()
+ .lint_root;
self.register_violations(&[UnsafetyViolation {
source_info,
description: Symbol::intern("borrow of packed field"),
fn register_violations(&mut self,
violations: &[UnsafetyViolation],
unsafe_blocks: &[(hir::HirId, bool)]) {
- let safety = self.source_scope_local_data[self.source_info.scope].safety;
+ let safety = self.body.source_scopes[self.source_info.scope]
+ .local_data
+ .as_ref()
+ .assert_crate_local()
+ .safety;
let within_unsafe = match safety {
// `unsafe` blocks are required in safe code
Safety::Safe => {
// `mir_built` force this.
let body = &tcx.mir_built(def_id).borrow();
- let source_scope_local_data = match body.source_scope_local_data {
- ClearCrossCrate::Set(ref data) => data,
- ClearCrossCrate::Clear => {
- debug!("unsafety_violations: {:?} - remote, skipping", def_id);
- return UnsafetyCheckResult {
- violations: Lrc::new([]),
- unsafe_blocks: Lrc::new([])
- }
- }
- };
-
let param_env = tcx.param_env(def_id);
let id = tcx.hir().as_local_hir_id(def_id).unwrap();
hir::BodyOwnerKind::Const |
hir::BodyOwnerKind::Static(_) => (true, false),
};
- let mut checker = UnsafetyChecker::new(
- const_context, min_const_fn,
- body, source_scope_local_data, tcx, param_env);
+ let mut checker = UnsafetyChecker::new(const_context, min_const_fn, body, tcx, param_env);
+ // mir_built ensures that body has a computed cache, so we don't (and can't) attempt to
+ // recompute it here.
+ let body = body.unwrap_read_only();
checker.visit_body(body);
check_unused_unsafe(tcx, def_id, &checker.used_unsafe, &mut checker.inherited_blocks);
//! [`FakeRead`]: rustc::mir::StatementKind::FakeRead
//! [`Nop`]: rustc::mir::StatementKind::Nop
-use rustc::mir::{BorrowKind, Rvalue, Location, Body};
+use rustc::mir::{BodyCache, BorrowKind, Rvalue, Location};
use rustc::mir::{Statement, StatementKind};
use rustc::mir::visit::MutVisitor;
use rustc::ty::TyCtxt;
}
impl<'tcx> MirPass<'tcx> for CleanupNonCodegenStatements {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(
+ &self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut BodyCache<'tcx>
+ ) {
let mut delete = DeleteNonCodegenStatements { tcx };
delete.visit_body(body);
}
use rustc::hir::def::DefKind;
use rustc::hir::def_id::DefId;
use rustc::mir::{
- AggregateKind, Constant, Location, Place, PlaceBase, Body, Operand, Rvalue, Local, UnOp,
- StatementKind, Statement, LocalKind, TerminatorKind, Terminator, ClearCrossCrate, SourceInfo,
- BinOp, SourceScope, SourceScopeLocalData, LocalDecl, BasicBlock, RETURN_PLACE,
+ AggregateKind, Constant, Location, Place, PlaceBase, Body, BodyCache, Operand, Local, UnOp,
+ Rvalue, StatementKind, Statement, LocalKind, TerminatorKind, Terminator, ClearCrossCrate,
+ SourceInfo, BinOp, SourceScope, SourceScopeData, LocalDecl, BasicBlock, ReadOnlyBodyCache,
+ read_only, RETURN_PLACE
};
use rustc::mir::visit::{
Visitor, PlaceContext, MutatingUseContext, MutVisitor, NonMutatingUseContext,
pub struct ConstProp;
impl<'tcx> MirPass<'tcx> for ConstProp {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(
+ &self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyCache<'tcx>
+ ) {
// will be evaluated by miri and produce its errors there
if source.promoted.is_some() {
return;
trace!("ConstProp starting for {:?}", source.def_id());
- // Steal some data we need from `body`.
- let source_scope_local_data = std::mem::replace(
- &mut body.source_scope_local_data,
- ClearCrossCrate::Clear
- );
-
let dummy_body =
&Body::new(
body.basic_blocks().clone(),
- Default::default(),
- ClearCrossCrate::Clear,
+ body.source_scopes.clone(),
body.local_decls.clone(),
Default::default(),
body.arg_count,
// That would require an uniform one-def no-mutation analysis
// and RPO (or recursing when needing the value of a local).
let mut optimization_finder = ConstPropagator::new(
- body,
+ read_only!(body),
dummy_body,
- source_scope_local_data,
tcx,
source
);
optimization_finder.visit_body(body);
- // put back the data we stole from `mir`
- let source_scope_local_data = optimization_finder.release_stolen_data();
- std::mem::replace(
- &mut body.source_scope_local_data,
- source_scope_local_data
- );
-
trace!("ConstProp done for {:?}", source.def_id());
}
}
throw_unsup!(ConstPropUnsupported("calling intrinsics isn't supported in ConstProp"));
}
+ fn assert_panic(
+ _ecx: &mut InterpCx<'mir, 'tcx, Self>,
+ _span: Span,
+ _msg: &rustc::mir::interpret::AssertMessage<'tcx>,
+ _unwind: Option<rustc::mir::BasicBlock>,
+ ) -> InterpResult<'tcx> {
+ bug!("panics terminators are not evaluated in ConstProp");
+ }
+
fn ptr_to_int(
_mem: &Memory<'mir, 'tcx, Self>,
_ptr: Pointer,
}
#[inline(always)]
- fn tag_allocation<'b>(
+ fn init_allocation_extra<'b>(
_memory_extra: &(),
_id: AllocId,
alloc: Cow<'b, Allocation>,
source: MirSource<'tcx>,
can_const_prop: IndexVec<Local, bool>,
param_env: ParamEnv<'tcx>,
- source_scope_local_data: ClearCrossCrate<IndexVec<SourceScope, SourceScopeLocalData>>,
+ // FIXME(eddyb) avoid cloning these two fields more than once,
+ // by accessing them through `ecx` instead.
+ source_scopes: IndexVec<SourceScope, SourceScopeData>,
local_decls: IndexVec<Local, LocalDecl<'tcx>>,
ret: Option<OpTy<'tcx, ()>>,
}
impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
fn new(
- body: &Body<'tcx>,
+ body: ReadOnlyBodyCache<'_, 'tcx>,
dummy_body: &'mir Body<'tcx>,
- source_scope_local_data: ClearCrossCrate<IndexVec<SourceScope, SourceScopeLocalData>>,
tcx: TyCtxt<'tcx>,
source: MirSource<'tcx>,
) -> ConstPropagator<'mir, 'tcx> {
source,
param_env,
can_const_prop,
- source_scope_local_data,
+ // FIXME(eddyb) avoid cloning these two fields more than once,
+ // by accessing them through `ecx` instead.
+ source_scopes: body.source_scopes.clone(),
//FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
local_decls: body.local_decls.clone(),
ret: ret.map(Into::into),
}
}
- fn release_stolen_data(self) -> ClearCrossCrate<IndexVec<SourceScope, SourceScopeLocalData>> {
- self.source_scope_local_data
- }
-
fn get_const(&self, local: Local) -> Option<Const<'tcx>> {
if local == RETURN_PLACE {
// Try to read the return place as an immediate so that if it is representable as a
F: FnOnce(&mut Self) -> InterpResult<'tcx, T>,
{
self.ecx.tcx.span = source_info.span;
- let lint_root = match self.source_scope_local_data {
- ClearCrossCrate::Set(ref ivs) => {
- //FIXME(#51314): remove this check
- if source_info.scope.index() >= ivs.len() {
- return None;
- }
- ivs[source_info.scope].lint_root
- },
+ // FIXME(eddyb) move this to the `Panic(_)` error case, so that
+ // `f(self)` is always called, and that the only difference when the
+ // scope's `local_data` is missing, is that the lint isn't emitted.
+ let lint_root = match &self.source_scopes[source_info.scope].local_data {
+ ClearCrossCrate::Set(data) => data.lint_root,
ClearCrossCrate::Clear => return None,
};
let r = match f(self) {
InterpError::*
};
match error.kind {
- Exit(_) => bug!("the CTFE program cannot exit"),
+ MachineStop(_) => bug!("ConstProp does not stop"),
// Some error shouldn't come up because creating them causes
// an allocation, which we should avoid. When that happens,
let right_size = r.layout.size;
let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size));
if r_bits.ok().map_or(false, |b| b >= left_bits as u128) {
- let source_scope_local_data = match self.source_scope_local_data {
- ClearCrossCrate::Set(ref data) => data,
+ let lint_root = match &self.source_scopes[source_info.scope].local_data {
+ ClearCrossCrate::Set(data) => data.lint_root,
ClearCrossCrate::Clear => return None,
};
let dir = if *op == BinOp::Shr {
} else {
"left"
};
- let hir_id = source_scope_local_data[source_info.scope].lint_root;
self.tcx.lint_hir(
::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
- hir_id,
+ lint_root,
span,
&format!("attempt to shift {} with overflow", dir));
return None;
impl CanConstProp {
/// returns true if `local` can be propagated
- fn check(body: &Body<'_>) -> IndexVec<Local, bool> {
+ fn check(body: ReadOnlyBodyCache<'_, '_>) -> IndexVec<Local, bool> {
let mut cpv = CanConstProp {
can_const_prop: IndexVec::from_elem(true, &body.local_decls),
found_assignment: IndexVec::from_elem(false, &body.local_decls),
//! (non-mutating) use of `SRC`. These restrictions are conservative and may be relaxed in the
//! future.
-use rustc::mir::{Constant, Local, LocalKind, Location, Place, Body, Operand, Rvalue, StatementKind};
+use rustc::mir::{
+ Constant, Local, LocalKind, Location, Place, Body, BodyCache, Operand, Rvalue,
+ StatementKind, read_only
+};
use rustc::mir::visit::MutVisitor;
use rustc::ty::TyCtxt;
use crate::transform::{MirPass, MirSource};
pub struct CopyPropagation;
impl<'tcx> MirPass<'tcx> for CopyPropagation {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(
+ &self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut BodyCache<'tcx>
+ ) {
// We only run when the MIR optimization level is > 1.
// This avoids a slow pass, and messing up debug info.
if tcx.sess.opts.debugging_opts.mir_opt_level <= 1 {
let mut def_use_analysis = DefUseAnalysis::new(body);
loop {
- def_use_analysis.analyze(body);
+ def_use_analysis.analyze(read_only!(body));
if eliminate_self_assignments(body, &def_use_analysis) {
- def_use_analysis.analyze(body);
+ def_use_analysis.analyze(read_only!(body));
}
let mut changed = false;
let maybe_action = match operand {
Operand::Copy(ref src_place) |
Operand::Move(ref src_place) => {
- Action::local_copy(&body, &def_use_analysis, src_place)
+ Action::local_copy(
+ &body,
+ &def_use_analysis,
+ src_place)
}
Operand::Constant(ref src_constant) => {
Action::constant(src_constant)
}
}
- changed =
- action.perform(body, &def_use_analysis, dest_local, location, tcx) || changed;
+ changed = action.perform(body, &def_use_analysis, dest_local, location, tcx)
+ || changed;
// FIXME(pcwalton): Update the use-def chains to delete the instructions instead of
// regenerating the chains.
break
}
fn perform(self,
- body: &mut Body<'tcx>,
+ body: &mut BodyCache<'tcx>,
def_use_analysis: &DefUseAnalysis,
dest_local: Local,
location: Location,
}
// Replace all uses of the destination local with the source local.
- def_use_analysis.replace_all_defs_and_uses_with(dest_local, body, src_local, tcx);
+ def_use_analysis
+ .replace_all_defs_and_uses_with(dest_local, body, src_local, tcx);
// Finally, zap the now-useless assignment instruction.
debug!(" Deleting assignment");
pub struct Deaggregator;
impl<'tcx> MirPass<'tcx> for Deaggregator {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(
+ &self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut BodyCache<'tcx>
+ ) {
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
let local_decls = &*local_decls;
for bb in basic_blocks {
use std::fs::File;
use std::io;
-use rustc::mir::Body;
+use rustc::mir::{Body, BodyCache};
use rustc::session::config::{OutputFilenames, OutputType};
use rustc::ty::TyCtxt;
use crate::transform::{MirPass, MirSource};
Cow::Borrowed(self.0)
}
- fn run_pass(&self, _tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, _body: &mut Body<'tcx>) {
- }
+ fn run_pass(
+ &self, _tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, _body: &mut BodyCache<'tcx>
+ ) {}
}
pub struct Disambiguator {
pub struct ElaborateDrops;
impl<'tcx> MirPass<'tcx> for ElaborateDrops {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyCache<'tcx>) {
debug!("elaborate_drops({:?} @ {:?})", src, body.span);
let def_id = src.def_id();
pub struct EraseRegions;
impl<'tcx> MirPass<'tcx> for EraseRegions {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyCache<'tcx>) {
EraseRegionsVisitor::new(tcx).visit_body(body);
}
}
VariantIdx::new(RETURNED) // state for returned
};
data.statements.push(self.set_discr(state, source_info));
- data.terminator.as_mut().unwrap().kind = TerminatorKind::Return;
+ data.terminator_mut().kind = TerminatorKind::Return;
}
self.super_basic_block_data(block, data);
fn make_generator_state_argument_indirect<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: DefId,
- body: &mut Body<'tcx>,
+ body: &mut BodyCache<'tcx>,
) {
let gen_ty = body.local_decls.raw[1].ty;
DerefArgVisitor { tcx }.visit_body(body);
}
-fn make_generator_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+fn make_generator_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut BodyCache<'tcx>) {
let ref_gen_ty = body.local_decls.raw[1].ty;
let pin_did = tcx.lang_items().pin_type().unwrap();
fn replace_result_variable<'tcx>(
ret_ty: Ty<'tcx>,
- body: &mut Body<'tcx>,
+ body: &mut BodyCache<'tcx>,
tcx: TyCtxt<'tcx>,
) -> Local {
let source_info = source_info(body);
fn locals_live_across_suspend_points(
tcx: TyCtxt<'tcx>,
- body: &Body<'tcx>,
+ body: ReadOnlyBodyCache<'_, 'tcx>,
source: MirSource<'tcx>,
movable: bool,
) -> LivenessInfo {
let dead_unwinds = BitSet::new_empty(body.basic_blocks().len());
let def_id = source.def_id();
+ let body_ref: &Body<'_> = &body;
// Calculate when MIR locals have live storage. This gives us an upper bound of their
// lifetimes.
- let storage_live_analysis = MaybeStorageLive::new(body);
+ let storage_live_analysis = MaybeStorageLive::new(body_ref);
let storage_live_results =
- do_dataflow(tcx, body, def_id, &[], &dead_unwinds, storage_live_analysis,
+ do_dataflow(tcx, body_ref, def_id, &[], &dead_unwinds, storage_live_analysis,
|bd, p| DebugFormatted::new(&bd.body().local_decls[p]));
- let mut storage_live_cursor = DataflowResultsCursor::new(&storage_live_results, body);
+ let mut storage_live_cursor = DataflowResultsCursor::new(&storage_live_results, body_ref);
// Find the MIR locals which do not use StorageLive/StorageDead statements.
// The storage of these locals are always live.
// Calculate the MIR locals which have been previously
// borrowed (even if they are still active).
- let borrowed_locals_analysis = HaveBeenBorrowedLocals::new(body);
+ let borrowed_locals_analysis = HaveBeenBorrowedLocals::new(body_ref);
let borrowed_locals_results =
- do_dataflow(tcx, body, def_id, &[], &dead_unwinds, borrowed_locals_analysis,
+ do_dataflow(tcx, body_ref, def_id, &[], &dead_unwinds, borrowed_locals_analysis,
|bd, p| DebugFormatted::new(&bd.body().local_decls[p]));
- let mut borrowed_locals_cursor = DataflowResultsCursor::new(&borrowed_locals_results, body);
+ let mut borrowed_locals_cursor = DataflowResultsCursor::new(&borrowed_locals_results, body_ref);
// Calculate the MIR locals that we actually need to keep storage around
// for.
let requires_storage_analysis = RequiresStorage::new(body, &borrowed_locals_results);
let requires_storage_results =
- do_dataflow(tcx, body, def_id, &[], &dead_unwinds, requires_storage_analysis,
+ do_dataflow(tcx, body_ref, def_id, &[], &dead_unwinds, requires_storage_analysis,
|bd, p| DebugFormatted::new(&bd.body().local_decls[p]));
- let mut requires_storage_cursor = DataflowResultsCursor::new(&requires_storage_results, body);
+ let mut requires_storage_cursor
+ = DataflowResultsCursor::new(&requires_storage_results, body_ref);
// Calculate the liveness of MIR locals ignoring borrows.
let mut live_locals = liveness::LiveVarSet::new_empty(body.local_decls.len());
tcx,
"generator_liveness",
source,
- body,
+ body_ref,
&liveness,
);
.collect();
let storage_conflicts = compute_storage_conflicts(
- body,
+ body_ref,
&live_locals,
&ignored,
requires_storage_results);
upvars: &Vec<Ty<'tcx>>,
interior: Ty<'tcx>,
movable: bool,
- body: &mut Body<'tcx>,
+ body: &mut BodyCache<'tcx>,
) -> (
FxHashMap<Local, (Ty<'tcx>, VariantIdx, usize)>,
GeneratorLayout<'tcx>,
// Use a liveness analysis to compute locals which are live across a suspension point
let LivenessInfo {
live_locals, live_locals_at_suspension_points, storage_conflicts, storage_liveness
- } = locals_live_across_suspend_points(tcx, body, source, movable);
+ } = locals_live_across_suspend_points(tcx, read_only!(body), source, movable);
// Erase regions from the types passed in from typeck so we can compare them with
// MIR types
}
fn insert_switch<'tcx>(
- body: &mut Body<'tcx>,
+ body: &mut BodyCache<'tcx>,
cases: Vec<(usize, BasicBlock)>,
transform: &TransformVisitor<'tcx>,
default: TerminatorKind<'tcx>,
}
}
-fn elaborate_generator_drops<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, body: &mut Body<'tcx>) {
+fn elaborate_generator_drops<'tcx>(
+ tcx: TyCtxt<'tcx>, def_id: DefId, body: &mut BodyCache<'tcx>
+) {
use crate::util::elaborate_drops::{elaborate_drop, Unwind};
use crate::util::patch::MirPatch;
use crate::shim::DropShimElaborator;
let gen = self_arg();
let mut elaborator = DropShimElaborator {
- body: body,
+ body,
patch: MirPatch::new(body),
tcx,
param_env
def_id: DefId,
source: MirSource<'tcx>,
gen_ty: Ty<'tcx>,
- body: &Body<'tcx>,
+ body: &mut BodyCache<'tcx>,
drop_clean: BasicBlock,
-) -> Body<'tcx> {
+) -> BodyCache<'tcx> {
let mut body = body.clone();
let source_info = source_info(&body);
body
}
-fn insert_term_block<'tcx>(body: &mut Body<'tcx>, kind: TerminatorKind<'tcx>) -> BasicBlock {
+fn insert_term_block<'tcx>(
+ body: &mut BodyCache<'tcx>, kind: TerminatorKind<'tcx>
+) -> BasicBlock {
let term_block = BasicBlock::new(body.basic_blocks().len());
let source_info = source_info(body);
body.basic_blocks_mut().push(BasicBlockData {
fn insert_panic_block<'tcx>(
tcx: TyCtxt<'tcx>,
- body: &mut Body<'tcx>,
+ body: &mut BodyCache<'tcx>,
message: AssertMessage<'tcx>,
) -> BasicBlock {
let assert_block = BasicBlock::new(body.basic_blocks().len());
transform: TransformVisitor<'tcx>,
def_id: DefId,
source: MirSource<'tcx>,
- body: &mut Body<'tcx>,
+ body: &mut BodyCache<'tcx>,
) {
// Poison the generator when it unwinds
for block in body.basic_blocks_mut() {
}
}
-fn insert_clean_drop(body: &mut Body<'_>) -> BasicBlock {
+fn insert_clean_drop(body: &mut BodyCache<'_>) -> BasicBlock {
let return_block = insert_term_block(body, TerminatorKind::Return);
// Create a block to destroy an unresumed generators. This can only destroy upvars.
}
fn create_cases<'tcx, F>(
- body: &mut Body<'tcx>,
+ body: &mut BodyCache<'tcx>,
transform: &TransformVisitor<'tcx>,
target: F,
) -> Vec<(usize, BasicBlock)>
}
impl<'tcx> MirPass<'tcx> for StateTransform {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(
+ &self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyCache<'tcx>
+ ) {
let yield_ty = if let Some(yield_ty) = body.yield_ty {
yield_ty
} else {
// Create a copy of our MIR and use it to create the drop shim for the generator
let drop_shim = create_generator_drop_shim(tcx,
- &transform,
- def_id,
- source,
- gen_ty,
- &body,
- drop_clean);
+ &transform,
+ def_id,
+ source,
+ gen_ty,
+ body,
+ drop_clean);
body.generator_drop = Some(box drop_shim);
}
impl<'tcx> MirPass<'tcx> for Inline {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(
+ &self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyCache<'tcx>
+ ) {
if tcx.sess.opts.debugging_opts.mir_opt_level >= 2 {
Inliner { tcx, source }.run_pass(body);
}
}
impl Inliner<'tcx> {
- fn run_pass(&self, caller_body: &mut Body<'tcx>) {
+ fn run_pass(&self, caller_body: &mut BodyCache<'tcx>) {
// Keep a queue of callsites to try inlining on. We take
// advantage of the fact that queries detect cycles here to
// allow us to try and fetch the fully optimized MIR of a
{
for (bb, bb_data) in caller_body.basic_blocks().iter_enumerated() {
if let Some(callsite) = self.get_valid_function_call(bb,
- bb_data,
- caller_body,
- param_env) {
+ bb_data,
+ caller_body,
+ param_env) {
callsites.push_back(callsite);
}
}
debug!("attempting to inline callsite {:?} - success", callsite);
// Add callsites from inlined function
- for (bb, bb_data) in caller_body.basic_blocks().iter_enumerated().skip(start) {
+ for (bb, bb_data) in caller_body.basic_blocks().iter_enumerated().skip(start)
+ {
if let Some(new_callsite) = self.get_valid_function_call(bb,
bb_data,
caller_body,
fn inline_call(&self,
callsite: CallSite<'tcx>,
- caller_body: &mut Body<'tcx>,
- mut callee_body: Body<'tcx>) -> bool {
+ caller_body: &mut BodyCache<'tcx>,
+ mut callee_body: BodyCache<'tcx>) -> bool {
let terminator = caller_body[callsite.bb].terminator.take().unwrap();
match terminator.kind {
// FIXME: Handle inlining of diverging calls
for mut scope in callee_body.source_scopes.iter().cloned() {
if scope.parent_scope.is_none() {
scope.parent_scope = Some(callsite.location.scope);
+ // FIXME(eddyb) is this really needed?
+ // (also note that it's always overwritten below)
scope.span = callee_body.span;
}
+ // FIXME(eddyb) this doesn't seem right at all.
+ // The inlined source scopes should probably be annotated as
+ // such, but also contain all of the original information.
scope.span = callsite.location.span;
let idx = caller_body.source_scopes.push(scope);
BorrowKind::Mut { allow_two_phase_borrow: false },
destination.0);
- let ty = dest.ty(caller_body, self.tcx);
+ let ty = dest.ty(&**caller_body, self.tcx);
let temp = LocalDecl::new_temp(ty, callsite.location.span);
&self,
args: Vec<Operand<'tcx>>,
callsite: &CallSite<'tcx>,
- caller_body: &mut Body<'tcx>,
+ caller_body: &mut BodyCache<'tcx>,
) -> Vec<Local> {
let tcx = self.tcx;
// and the vector is `[closure_ref, tmp0, tmp1, tmp2]`.
if tcx.is_closure(callsite.callee) {
let mut args = args.into_iter();
- let self_ = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body);
- let tuple = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body);
+ let self_
+ = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body);
+ let tuple
+ = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body);
assert!(args.next().is_none());
let tuple = Place::from(tuple);
- let tuple_tys = if let ty::Tuple(s) = tuple.ty(caller_body, tcx).ty.kind {
+ let tuple_tys = if let ty::Tuple(s) = tuple.ty(&**caller_body, tcx).ty.kind {
s
} else {
bug!("Closure arguments are not passed as a tuple");
&self,
arg: Operand<'tcx>,
callsite: &CallSite<'tcx>,
- caller_body: &mut Body<'tcx>,
+ caller_body: &mut BodyCache<'tcx>,
) -> Local {
// FIXME: Analysis of the usage of the arguments to avoid
// unnecessary temporaries.
// Otherwise, create a temporary for the arg
let arg = Rvalue::Use(arg);
- let ty = arg.ty(caller_body, self.tcx);
+ let ty = arg.ty(&**caller_body, self.tcx);
let arg_tmp = LocalDecl::new_temp(ty, callsite.location.span);
let arg_tmp = caller_body.local_decls.push(arg_tmp);
//! Performs various peephole optimizations.
use rustc::mir::{
- Constant, Location, Place, PlaceBase, PlaceRef, Body, Operand, ProjectionElem, Rvalue, Local
+ Constant, Location, Place, PlaceBase, PlaceRef, Body, BodyCache, Operand, ProjectionElem,
+ Rvalue, Local, read_only
};
use rustc::mir::visit::{MutVisitor, Visitor};
use rustc::ty::{self, TyCtxt};
pub struct InstCombine;
impl<'tcx> MirPass<'tcx> for InstCombine {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyCache<'tcx>) {
// We only run when optimizing MIR (at any level).
if tcx.sess.opts.debugging_opts.mir_opt_level == 0 {
return
// read-only so that we can do global analyses on the MIR in the process (e.g.
// `Place::ty()`).
let optimizations = {
+ let read_only_cache = read_only!(body);
let mut optimization_finder = OptimizationFinder::new(body, tcx);
- optimization_finder.visit_body(body);
+ optimization_finder.visit_body(read_only_cache);
optimization_finder.optimizations
};
use crate::{build, shim};
use rustc_index::vec::IndexVec;
use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
-use rustc::mir::{Body, MirPhase, Promoted, ConstQualifs};
+use rustc::mir::{BodyCache, MirPhase, Promoted, ConstQualifs};
use rustc::ty::{TyCtxt, InstanceDef, TypeFoldable};
use rustc::ty::query::Providers;
use rustc::ty::steal::Steal;
tcx.arena.alloc(set)
}
-fn mir_built(tcx: TyCtxt<'_>, def_id: DefId) -> &Steal<Body<'_>> {
+fn mir_built(tcx: TyCtxt<'_>, def_id: DefId) -> &Steal<BodyCache<'_>> {
let mir = build::mir_build(tcx, def_id);
tcx.alloc_steal_mir(mir)
}
default_name::<Self>()
}
- fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>);
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyCache<'tcx>);
}
pub fn run_passes(
tcx: TyCtxt<'tcx>,
- body: &mut Body<'tcx>,
+ body: &mut BodyCache<'tcx>,
instance: InstanceDef<'tcx>,
promoted: Option<Promoted>,
mir_phase: MirPhase,
}
let item = check_consts::Item {
- body,
+ body: body.unwrap_read_only(),
tcx,
def_id,
const_kind,
validator.qualifs_in_return_place().into()
}
-fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> &Steal<Body<'_>> {
+fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> &Steal<BodyCache<'_>> {
// Unsafety check uses the raw mir, so make sure it is run
let _ = tcx.unsafety_check_result(def_id);
&rustc_peek::SanityCheck,
&uniform_array_move_out::UniformArrayMoveOut,
]);
+ body.ensure_predecessors();
tcx.alloc_steal_mir(body)
}
fn mir_validated(
tcx: TyCtxt<'tcx>,
def_id: DefId,
-) -> (&'tcx Steal<Body<'tcx>>, &'tcx Steal<IndexVec<Promoted, Body<'tcx>>>) {
+) -> (&'tcx Steal<BodyCache<'tcx>>, &'tcx Steal<IndexVec<Promoted, BodyCache<'tcx>>>) {
// Ensure that we compute the `mir_const_qualif` for constants at
// this point, before we steal the mir-const result.
let _ = tcx.mir_const_qualif(def_id);
&promote_pass,
&simplify::SimplifyCfg::new("qualify-consts"),
]);
+
let promoted = promote_pass.promoted_fragments.into_inner();
(tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted))
}
fn run_optimization_passes<'tcx>(
tcx: TyCtxt<'tcx>,
- body: &mut Body<'tcx>,
+ body: &mut BodyCache<'tcx>,
def_id: DefId,
promoted: Option<Promoted>,
) {
]);
}
-fn optimized_mir(tcx: TyCtxt<'_>, def_id: DefId) -> &Body<'_> {
+fn optimized_mir(tcx: TyCtxt<'_>, def_id: DefId) -> &BodyCache<'_> {
if tcx.is_constructor(def_id) {
// There's no reason to run all of the MIR passes on constructors when
// we can just output the MIR we want directly. This also saves const
let (body, _) = tcx.mir_validated(def_id);
let mut body = body.steal();
run_optimization_passes(tcx, &mut body, def_id, None);
+ body.ensure_predecessors();
tcx.arena.alloc(body)
}
-fn promoted_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx IndexVec<Promoted, Body<'tcx>> {
+fn promoted_mir(tcx: TyCtxt<'_>, def_id: DefId) -> &IndexVec<Promoted, BodyCache<'_>> {
if tcx.is_constructor(def_id) {
return tcx.intern_promoted(IndexVec::new());
}
for (p, mut body) in promoted.iter_enumerated_mut() {
run_optimization_passes(tcx, &mut body, def_id, Some(p));
+ body.ensure_predecessors();
}
tcx.intern_promoted(promoted)
}
impl<'tcx> MirPass<'tcx> for NoLandingPads<'tcx> {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyCache<'tcx>) {
no_landing_pads(tcx, body)
}
}
-pub fn no_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+pub fn no_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body: &mut BodyCache<'tcx>) {
if tcx.sess.no_landing_pads() {
NoLandingPads::new(tcx).visit_body(body);
}
/// newly created `StaticKind::Promoted`.
#[derive(Default)]
pub struct PromoteTemps<'tcx> {
- pub promoted_fragments: Cell<IndexVec<Promoted, Body<'tcx>>>,
+ pub promoted_fragments: Cell<IndexVec<Promoted, BodyCache<'tcx>>>,
}
impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyCache<'tcx>) {
// There's not really any point in promoting errorful MIR.
//
// This does not include MIR that failed const-checking, which we still try to promote.
let mut rpo = traversal::reverse_postorder(body);
let (temps, all_candidates) = collect_temps_and_candidates(tcx, body, &mut rpo);
- let promotable_candidates = validate_candidates(tcx, body, def_id, &temps, &all_candidates);
+ let promotable_candidates
+ = validate_candidates(tcx, read_only!(body), def_id, &temps, &all_candidates);
let promoted = promote_candidates(def_id, body, tcx, temps, promotable_candidates);
self.promoted_fragments.set(promoted);
while let [proj_base @ .., elem] = place_projection {
// FIXME(eddyb) this is probably excessive, with
// the exception of `union` member accesses.
- let ty =
- Place::ty_from(&place.base, proj_base, self.body, self.tcx)
- .projection_ty(self.tcx, elem)
- .ty;
+ let ty = Place::ty_from(
+ &place.base,
+ proj_base,
+ &*self.body,
+ self.tcx
+ )
+ .projection_ty(self.tcx, elem)
+ .ty;
if ty.is_freeze(self.tcx, self.param_env, DUMMY_SP) {
has_mut_interior = false;
break;
}
if let BorrowKind::Mut { .. } = kind {
- let ty = place.ty(self.body, self.tcx).ty;
+ let ty = place.ty(&*self.body, self.tcx).ty;
// In theory, any zero-sized value could be borrowed
// mutably without consequences. However, only &mut []
ProjectionElem::Field(..) => {
if self.const_kind.is_none() {
let base_ty =
- Place::ty_from(place.base, proj_base, self.body, self.tcx).ty;
+ Place::ty_from(place.base, proj_base, &*self.body, self.tcx).ty;
if let Some(def) = base_ty.ty_adt_def() {
// No promotion of union field accesses.
if def.is_union() {
fn validate_rvalue(&self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> {
match *rvalue {
Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if self.const_kind.is_none() => {
- let operand_ty = operand.ty(self.body, self.tcx);
+ let operand_ty = operand.ty(&*self.body, self.tcx);
let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
match (cast_in, cast_out) {
}
Rvalue::BinaryOp(op, ref lhs, _) if self.const_kind.is_none() => {
- if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind {
+ if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(&*self.body, self.tcx).kind {
assert!(op == BinOp::Eq || op == BinOp::Ne ||
op == BinOp::Le || op == BinOp::Lt ||
op == BinOp::Ge || op == BinOp::Gt ||
Rvalue::Ref(_, kind, place) => {
if let BorrowKind::Mut { .. } = kind {
- let ty = place.ty(self.body, self.tcx).ty;
+ let ty = place.ty(&*self.body, self.tcx).ty;
// In theory, any zero-sized value could be borrowed
// mutably without consequences. However, only &mut []
let mut place = place.as_ref();
if let [proj_base @ .., ProjectionElem::Deref] = &place.projection {
let base_ty =
- Place::ty_from(&place.base, proj_base, self.body, self.tcx).ty;
+ Place::ty_from(&place.base, proj_base, &*self.body, self.tcx).ty;
if let ty::Ref(..) = base_ty.kind {
place = PlaceRef {
base: &place.base,
while let [proj_base @ .., elem] = place_projection {
// FIXME(eddyb) this is probably excessive, with
// the exception of `union` member accesses.
- let ty = Place::ty_from(place.base, proj_base, self.body, self.tcx)
+ let ty = Place::ty_from(place.base, proj_base, &*self.body, self.tcx)
.projection_ty(self.tcx, elem)
.ty;
if ty.is_freeze(self.tcx, self.param_env, DUMMY_SP) {
callee: &Operand<'tcx>,
args: &[Operand<'tcx>],
) -> Result<(), Unpromotable> {
- let fn_ty = callee.ty(self.body, self.tcx);
+ let fn_ty = callee.ty(&*self.body, self.tcx);
if !self.explicit && self.const_kind.is_none() {
if let ty::FnDef(def_id, _) = fn_ty.kind {
// FIXME(eddyb) remove the differences for promotability in `static`, `const`, `const fn`.
pub fn validate_candidates(
tcx: TyCtxt<'tcx>,
- body: &Body<'tcx>,
+ body: ReadOnlyBodyCache<'_, 'tcx>,
def_id: DefId,
temps: &IndexVec<Local, TempState>,
candidates: &[Candidate],
struct Promoter<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
- source: &'a mut Body<'tcx>,
- promoted: Body<'tcx>,
+ source: &'a mut BodyCache<'tcx>,
+ promoted: BodyCache<'tcx>,
temps: &'a mut IndexVec<Local, TempState>,
/// If true, all nested temps are also kept in the
def_id: DefId,
candidate: Candidate,
next_promoted_id: usize,
- ) -> Option<Body<'tcx>> {
+ ) -> Option<BodyCache<'tcx>> {
let mut operand = {
let promoted = &mut self.promoted;
let promoted_id = Promoted::new(next_promoted_id);
pub fn promote_candidates<'tcx>(
def_id: DefId,
- body: &mut Body<'tcx>,
+ body: &mut BodyCache<'tcx>,
tcx: TyCtxt<'tcx>,
mut temps: IndexVec<Local, TempState>,
candidates: Vec<Candidate>,
-) -> IndexVec<Promoted, Body<'tcx>> {
+) -> IndexVec<Promoted, BodyCache<'tcx>> {
// Visit candidates in reverse, in case they're nested.
debug!("promote_candidates({:?})", candidates);
).collect();
let promoter = Promoter {
- promoted: Body::new(
+ promoted: BodyCache::new(Body::new(
IndexVec::new(),
// FIXME: maybe try to filter this to avoid blowing up
// memory usage?
body.source_scopes.clone(),
- body.source_scope_local_data.clone(),
initial_locals,
IndexVec::new(),
0,
body.span,
vec![],
body.generator_kind,
- ),
+ )),
tcx,
source: body,
temps: &mut temps,
crate fn should_suggest_const_in_array_repeat_expressions_attribute<'tcx>(
tcx: TyCtxt<'tcx>,
mir_def_id: DefId,
- body: &Body<'tcx>,
+ body: ReadOnlyBodyCache<'_, 'tcx>,
operand: &Operand<'tcx>,
) -> bool {
- let mut rpo = traversal::reverse_postorder(body);
- let (temps, _) = collect_temps_and_candidates(tcx, body, &mut rpo);
+ let mut rpo = traversal::reverse_postorder(&body);
+ let (temps, _) = collect_temps_and_candidates(tcx, &body, &mut rpo);
let validator = Validator {
item: Item::new(tcx, mir_def_id, body),
temps: &temps,
/// code for these.
pub struct RemoveNoopLandingPads;
-pub fn remove_noop_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+pub fn remove_noop_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body: &mut BodyCache<'tcx>) {
if tcx.sess.no_landing_pads() {
return
}
}
impl<'tcx> MirPass<'tcx> for RemoveNoopLandingPads {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut BodyCache<'tcx>) {
remove_noop_landing_pads(tcx, body);
}
}
}
}
- fn remove_nop_landing_pads(&self, body: &mut Body<'_>) {
+ fn remove_nop_landing_pads(&self, body: &mut BodyCache<'_>) {
// make sure there's a single resume block
let resume_block = {
let patch = MirPatch::new(body);
use rustc::ty::{self, TyCtxt, Ty};
use rustc::hir::def_id::DefId;
-use rustc::mir::{self, Body, Location, Local};
+use rustc::mir::{self, Body, BodyCache, Location, Local};
use rustc_index::bit_set::BitSet;
use crate::transform::{MirPass, MirSource};
pub struct SanityCheck;
impl<'tcx> MirPass<'tcx> for SanityCheck {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyCache<'tcx>) {
let def_id = src.def_id();
if !tcx.has_attr(def_id, sym::rustc_mir) {
debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_uninits);
}
if has_rustc_mir_with(&attributes, sym::rustc_peek_definite_init).is_some() {
- sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_def_inits);
+ sanity_check_via_rustc_peek(
+ tcx,
+ body,
+ def_id,
+ &attributes,
+ &flow_def_inits);
}
if has_rustc_mir_with(&attributes, sym::rustc_peek_indirectly_mutable).is_some() {
- sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_indirectly_mut);
+ sanity_check_via_rustc_peek(
+ tcx,
+ body,
+ def_id,
+ &attributes,
+ &flow_indirectly_mut);
}
if has_rustc_mir_with(&attributes, sym::stop_after_dataflow).is_some() {
tcx.sess.fatal("stop_after_dataflow ended compilation");
}
}
-pub fn simplify_cfg(body: &mut Body<'_>) {
+pub fn simplify_cfg(body: &mut BodyCache<'_>) {
CfgSimplifier::new(body).simplify();
remove_dead_blocks(body);
Cow::Borrowed(&self.label)
}
- fn run_pass(&self, _tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(
+ &self, _tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut BodyCache<'tcx>
+ ) {
debug!("SimplifyCfg({:?}) - simplifying {:?}", self.label, body);
simplify_cfg(body);
}
}
impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
- pub fn new(body: &'a mut Body<'tcx>) -> Self {
+ pub fn new(body: &'a mut BodyCache<'tcx>) -> Self {
let mut pred_count = IndexVec::from_elem(0u32, body.basic_blocks());
// we can't use mir.predecessors() here because that counts
changed |= inner_changed;
}
- self.basic_blocks[bb].statements.extend(new_stmts);
- self.basic_blocks[bb].terminator = Some(terminator);
+ let data = &mut self.basic_blocks[bb];
+ data.statements.extend(new_stmts);
+ data.terminator = Some(terminator);
changed |= inner_changed;
}
}
}
-pub fn remove_dead_blocks(body: &mut Body<'_>) {
+pub fn remove_dead_blocks(body: &mut BodyCache<'_>) {
let mut seen = BitSet::new_empty(body.basic_blocks().len());
for (bb, _) in traversal::preorder(body) {
seen.insert(bb.index());
for alive_index in seen.iter() {
replacements[alive_index] = BasicBlock::new(used_blocks);
if alive_index != used_blocks {
- // Swap the next alive block data with the current available slot. Since alive_index is
- // non-decreasing this is a valid operation.
+ // Swap the next alive block data with the current available slot. Since
+ // alive_index is non-decreasing this is a valid operation.
basic_blocks.raw.swap(alive_index, used_blocks);
}
used_blocks += 1;
pub struct SimplifyLocals;
impl<'tcx> MirPass<'tcx> for SimplifyLocals {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(
+ &self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyCache<'tcx>
+ ) {
trace!("running SimplifyLocals on {:?}", source);
let locals = {
+ let read_only_cache = read_only!(body);
let mut marker = DeclMarker {
locals: BitSet::new_empty(body.local_decls.len()),
body,
};
- marker.visit_body(body);
+ marker.visit_body(read_only_cache);
// Return pointer and arguments are always live
marker.locals.insert(RETURN_PLACE);
for arg in body.args_iter() {
Cow::Borrowed(&self.label)
}
- fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyCache<'tcx>) {
let param_env = tcx.param_env(src.def_id());
for block in body.basic_blocks_mut() {
let terminator = block.terminator_mut();
pub struct SimplifyArmIdentity;
impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
- fn run_pass(&self, _: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(&self, _: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyCache<'tcx>) {
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
for bb in basic_blocks {
// Need 3 statements:
pub struct SimplifyBranchSame;
impl<'tcx> MirPass<'tcx> for SimplifyBranchSame {
- fn run_pass(&self, _: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(&self, _: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyCache<'tcx>) {
let mut did_remove_blocks = false;
let bbs = body.basic_blocks_mut();
for bb_idx in bbs.indices() {
pub struct UniformArrayMoveOut;
impl<'tcx> MirPass<'tcx> for UniformArrayMoveOut {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyCache<'tcx>) {
let mut patch = MirPatch::new(body);
let param_env = tcx.param_env(src.def_id());
{
- let mut visitor = UniformArrayMoveOutVisitor{body, patch: &mut patch, tcx, param_env};
- visitor.visit_body(body);
+ let read_only_cache = read_only!(body);
+ let mut visitor
+ = UniformArrayMoveOutVisitor{ body, patch: &mut patch, tcx, param_env};
+ visitor.visit_body(read_only_cache);
}
patch.apply(body);
}
}
impl<'tcx> MirPass<'tcx> for RestoreSubsliceArrayMoveOut<'tcx> {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyCache<'tcx>) {
let mut patch = MirPatch::new(body);
let param_env = tcx.param_env(src.def_id());
{
+ let read_only_cache = read_only!(body);
let mut visitor = RestoreDataCollector {
locals_use: IndexVec::from_elem(LocalUse::new(), &body.local_decls),
candidates: vec![],
};
- visitor.visit_body(body);
+ visitor.visit_body(read_only_cache);
for candidate in &visitor.candidates {
let statement = &body[candidate.block].statements[candidate.statement_index];
let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2);
let opt_size = opt_src_place.and_then(|src_place| {
- let src_ty =
- Place::ty_from(src_place.base, src_place.projection, body, tcx).ty;
+ let src_ty = Place::ty_from(
+ src_place.base,
+ src_place.projection,
+ &**body,
+ tcx
+ ).ty;
if let ty::Array(_, ref size_o) = src_ty.kind {
size_o.try_eval_usize(tcx, param_env)
} else {
use crate::transform::{MirPass, MirSource};
use rustc::mir::{
- BasicBlock, BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, TerminatorKind,
+ BasicBlock, BasicBlockData, Body, BodyCache, Local, Operand, Rvalue, StatementKind,
+ TerminatorKind,
};
use rustc::ty::layout::{Abi, TyLayout, Variants};
use rustc::ty::{Ty, TyCtxt};
}
impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyCache<'tcx>) {
if source.promoted.is_some() {
return;
}
use rustc::mir::{Local, Location};
-use rustc::mir::Body;
+use rustc::mir::ReadOnlyBodyCache;
use rustc::mir::visit::PlaceContext;
use rustc::mir::visit::Visitor;
fn find_assignments(&self, local: Local) -> Vec<Location>;
}
-impl<'tcx> FindAssignments for Body<'tcx>{
+impl<'a, 'tcx> FindAssignments for ReadOnlyBodyCache<'a, 'tcx>{
fn find_assignments(&self, local: Local) -> Vec<Location>{
let mut visitor = FindLocalAssignmentVisitor{ needle: local, locations: vec![]};
- visitor.visit_body(self);
+ visitor.visit_body(*self);
visitor.locations
}
}
//! Def-use analysis.
-use rustc::mir::{Body, Local, Location, PlaceElem, VarDebugInfo};
+use rustc::mir::{Body, BodyCache, Local, Location, PlaceElem, ReadOnlyBodyCache, VarDebugInfo};
use rustc::mir::visit::{PlaceContext, MutVisitor, Visitor};
use rustc::ty::TyCtxt;
use rustc_index::vec::IndexVec;
}
}
- pub fn analyze(&mut self, body: &Body<'_>) {
+ pub fn analyze(&mut self, body: ReadOnlyBodyCache<'_, '_>) {
self.clear();
let mut finder = DefUseFinder {
fn mutate_defs_and_uses(
&self,
local: Local,
- body: &mut Body<'tcx>,
+ body: &mut BodyCache<'tcx>,
new_local: Local,
tcx: TyCtxt<'tcx>,
) {
- let mut visitor = MutateUseVisitor::new(local, new_local, body, tcx);
+ let mut visitor = MutateUseVisitor::new(local, new_local, tcx);
let info = &self.info[local];
for place_use in &info.defs_and_uses {
visitor.visit_location(body, place_use.location)
// FIXME(pcwalton): this should update the def-use chains.
pub fn replace_all_defs_and_uses_with(&self,
local: Local,
- body: &mut Body<'tcx>,
+ body: &mut BodyCache<'tcx>,
new_local: Local,
tcx: TyCtxt<'tcx>) {
self.mutate_defs_and_uses(local, body, new_local, tcx)
fn new(
query: Local,
new_local: Local,
- _: &Body<'tcx>,
tcx: TyCtxt<'tcx>,
) -> MutateUseVisitor<'tcx> {
MutateUseVisitor { query, new_local, tcx }
/// Computes which local variables are live within the given function
/// `mir`, including drops.
pub fn liveness_of_locals(
- body: &Body<'_>,
+ body: ReadOnlyBodyCache<'_, '_>,
) -> LivenessResult {
let num_live_vars = body.local_decls.len();
// FIXME(ecstaticmorse): Reverse post-order on the reverse CFG may generate a better iteration
// order when cycles are present, but the overhead of computing the reverse CFG may outweigh
// any benefits. Benchmark this and find out.
- let mut dirty_queue: WorkQueue<BasicBlock> = WorkQueue::with_none(body.basic_blocks().len());
- for (bb, _) in traversal::postorder(body) {
+ let mut dirty_queue: WorkQueue<BasicBlock>
+ = WorkQueue::with_none(body.basic_blocks().len());
+ for (bb, _) in traversal::postorder(&body) {
dirty_queue.insert(bb);
}
self.make_nop.push(loc);
}
- pub fn apply(self, body: &mut Body<'tcx>) {
+ pub fn apply(self, body: &mut BodyCache<'tcx>) {
debug!("MirPatch: make nops at: {:?}", self.make_nop);
for loc in self.make_nop {
body.make_statement_nop(loc);
if !attr.has_name(sym::cfg_attr) {
return vec![attr];
}
- if attr.get_normal_item().tokens.is_empty() {
+ if let ast::MacArgs::Empty = attr.get_normal_item().args {
self.sess.span_diagnostic
.struct_span_err(
attr.span,
) -> PResult<'a, T> {
let mut parser = Parser::new(
sess,
- attr.get_normal_item().tokens.clone(),
+ // FIXME(#66940, Centril | petrochenkov): refactor this function so it doesn't
+ // require reconstructing and immediately re-parsing delimiters.
+ attr.get_normal_item().args.outer_tokens(),
None,
false,
false,
brackets.push(stream);
}
- brackets.push(item.tokens.clone());
+ brackets.push(item.args.outer_tokens());
// The span we list here for `#` and for `[ ... ]` are both wrong in
// that it encompasses more than each token, but it hopefully is "good
use syntax::attr;
use syntax::ast;
use syntax::util::comments;
-use syntax::token::{self, Nonterminal, DelimToken};
-use syntax::tokenstream::{TokenStream, TokenTree};
+use syntax::token::{self, Nonterminal};
use syntax_pos::{Span, Symbol};
use errors::PResult;
item
} else {
let path = self.parse_path(PathStyle::Mod)?;
- let tokens = if self.check(&token::OpenDelim(DelimToken::Paren)) ||
- self.check(&token::OpenDelim(DelimToken::Bracket)) ||
- self.check(&token::OpenDelim(DelimToken::Brace)) {
- self.parse_token_tree().into()
- } else if self.eat(&token::Eq) {
- let eq = TokenTree::token(token::Eq, self.prev_span);
- let mut is_interpolated_expr = false;
- if let token::Interpolated(nt) = &self.token.kind {
- if let token::NtExpr(..) = **nt {
- is_interpolated_expr = true;
- }
- }
- let token_tree = if is_interpolated_expr {
- // We need to accept arbitrary interpolated expressions to continue
- // supporting things like `doc = $expr` that work on stable.
- // Non-literal interpolated expressions are rejected after expansion.
- self.parse_token_tree()
- } else {
- self.parse_unsuffixed_lit()?.token_tree()
- };
- TokenStream::new(vec![eq.into(), token_tree.into()])
- } else {
- TokenStream::default()
- };
- ast::AttrItem { path, tokens }
+ let args = self.parse_attr_args()?;
+ ast::AttrItem { path, args }
})
}
Ok(attrs)
}
- fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
+ pub(super) fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
let lit = self.parse_lit()?;
debug!("checking if {:?} is unusuffixed", lit);
// `!`, as an operator, is prefix, so we know this isn't that.
if self.eat(&token::Not) {
// MACRO INVOCATION expression
- let (delim, tts) = self.expect_delimited_token_tree()?;
+ let args = self.parse_mac_args()?;
hi = self.prev_span;
ex = ExprKind::Mac(Mac {
path,
- tts,
- delim,
- span: lo.to(hi),
+ args,
prior_type_ascription: self.last_type_ascription,
});
} else if self.check(&token::OpenDelim(token::Brace)) {
use syntax::ast::{PathSegment, IsAuto, Constness, IsAsync, Unsafety, Defaultness, Extern, StrLit};
use syntax::ast::{Visibility, VisibilityKind, Mutability, FnHeader, ForeignItem, ForeignItemKind};
use syntax::ast::{Ty, TyKind, Generics, TraitRef, EnumDef, Variant, VariantData, StructField};
-use syntax::ast::{Mac, MacDelimiter, Block, BindingMode, FnDecl, FnSig, SelfKind, Param};
+use syntax::ast::{Mac, MacArgs, MacDelimiter, Block, BindingMode, FnDecl, FnSig, SelfKind, Param};
use syntax::print::pprust;
use syntax::ptr::P;
use syntax::ThinVec;
use syntax::token;
-use syntax::tokenstream::{TokenTree, TokenStream};
+use syntax::tokenstream::{DelimSpan, TokenTree, TokenStream};
use syntax::source_map::{self, respan, Span};
use syntax::struct_span_err;
use syntax_pos::BytePos;
let prev_span = self.prev_span;
self.complain_if_pub_macro(&visibility.node, prev_span);
- let mac_lo = self.token.span;
-
// Item macro
let path = self.parse_path(PathStyle::Mod)?;
self.expect(&token::Not)?;
- let (delim, tts) = self.expect_delimited_token_tree()?;
- if delim != MacDelimiter::Brace && !self.eat(&token::Semi) {
+ let args = self.parse_mac_args()?;
+ if args.need_semicolon() && !self.eat(&token::Semi) {
self.report_invalid_macro_expansion_item();
}
let hi = self.prev_span;
let mac = Mac {
path,
- tts,
- delim,
- span: mac_lo.to(hi),
+ args,
prior_type_ascription: self.last_type_ascription,
};
let item =
if self.token.is_path_start() &&
!(self.is_async_fn() && self.token.span.rust_2015()) {
let prev_span = self.prev_span;
- let lo = self.token.span;
let path = self.parse_path(PathStyle::Mod)?;
if path.segments.len() == 1 {
*at_end = true;
// eat a matched-delimiter token tree:
- let (delim, tts) = self.expect_delimited_token_tree()?;
- if delim != MacDelimiter::Brace {
+ let args = self.parse_mac_args()?;
+ if args.need_semicolon() {
self.expect_semi()?;
}
Ok(Some(Mac {
path,
- tts,
- delim,
- span: lo.to(self.prev_span),
+ args,
prior_type_ascription: self.last_type_ascription,
}))
} else {
vis: &Visibility,
lo: Span
) -> PResult<'a, Option<P<Item>>> {
- let token_lo = self.token.span;
let (ident, def) = if self.eat_keyword(kw::Macro) {
let ident = self.parse_ident()?;
- let tokens = if self.check(&token::OpenDelim(token::Brace)) {
- match self.parse_token_tree() {
- TokenTree::Delimited(_, _, tts) => tts,
- _ => unreachable!(),
- }
+ let body = if self.check(&token::OpenDelim(token::Brace)) {
+ self.parse_mac_args()?
} else if self.check(&token::OpenDelim(token::Paren)) {
- let args = self.parse_token_tree();
+ let params = self.parse_token_tree();
+ let pspan = params.span();
let body = if self.check(&token::OpenDelim(token::Brace)) {
self.parse_token_tree()
} else {
- self.unexpected()?;
- unreachable!()
+ return self.unexpected();
};
- TokenStream::new(vec![
- args.into(),
- TokenTree::token(token::FatArrow, token_lo.to(self.prev_span)).into(),
+ let bspan = body.span();
+ let tokens = TokenStream::new(vec![
+ params.into(),
+ TokenTree::token(token::FatArrow, pspan.between(bspan)).into(),
body.into(),
- ])
+ ]);
+ let dspan = DelimSpan::from_pair(pspan.shrink_to_lo(), bspan.shrink_to_hi());
+ P(MacArgs::Delimited(dspan, MacDelimiter::Brace, tokens))
} else {
- self.unexpected()?;
- unreachable!()
+ return self.unexpected();
};
- (ident, ast::MacroDef { tokens: tokens.into(), legacy: false })
+ (ident, ast::MacroDef { body, legacy: false })
} else if self.check_keyword(sym::macro_rules) &&
self.look_ahead(1, |t| *t == token::Not) &&
self.look_ahead(2, |t| t.is_ident()) {
self.bump();
let ident = self.parse_ident()?;
- let (delim, tokens) = self.expect_delimited_token_tree()?;
- if delim != MacDelimiter::Brace && !self.eat(&token::Semi) {
+ let body = self.parse_mac_args()?;
+ if body.need_semicolon() && !self.eat(&token::Semi) {
self.report_invalid_macro_expansion_item();
}
- (ident, ast::MacroDef { tokens, legacy: true })
+ (ident, ast::MacroDef { body, legacy: true })
} else {
return Ok(None);
};
use syntax::ast::{
self, DUMMY_NODE_ID, AttrStyle, Attribute, CrateSugar, Extern, Ident, StrLit,
- IsAsync, MacDelimiter, Mutability, Visibility, VisibilityKind, Unsafety,
+ IsAsync, MacArgs, MacDelimiter, Mutability, Visibility, VisibilityKind, Unsafety,
};
use syntax::print::pprust;
}
}
- fn expect_delimited_token_tree(&mut self) -> PResult<'a, (MacDelimiter, TokenStream)> {
- let delim = match self.token.kind {
- token::OpenDelim(delim) => delim,
- _ => {
- let msg = "expected open delimiter";
- let mut err = self.fatal(msg);
- err.span_label(self.token.span, msg);
- return Err(err)
+ fn parse_mac_args(&mut self) -> PResult<'a, P<MacArgs>> {
+ self.parse_mac_args_common(true).map(P)
+ }
+
+ fn parse_attr_args(&mut self) -> PResult<'a, MacArgs> {
+ self.parse_mac_args_common(false)
+ }
+
+ fn parse_mac_args_common(&mut self, delimited_only: bool) -> PResult<'a, MacArgs> {
+ Ok(if self.check(&token::OpenDelim(DelimToken::Paren)) ||
+ self.check(&token::OpenDelim(DelimToken::Bracket)) ||
+ self.check(&token::OpenDelim(DelimToken::Brace)) {
+ match self.parse_token_tree() {
+ TokenTree::Delimited(dspan, delim, tokens) =>
+ // We've confirmed above that there is a delimiter so unwrapping is OK.
+ MacArgs::Delimited(dspan, MacDelimiter::from_token(delim).unwrap(), tokens),
+ _ => unreachable!(),
}
- };
- let tts = match self.parse_token_tree() {
- TokenTree::Delimited(_, _, tts) => tts,
- _ => unreachable!(),
- };
- let delim = match delim {
- token::Paren => MacDelimiter::Parenthesis,
- token::Bracket => MacDelimiter::Bracket,
- token::Brace => MacDelimiter::Brace,
- token::NoDelim => self.bug("unexpected no delimiter"),
- };
- Ok((delim, tts.into()))
+ } else if !delimited_only {
+ if self.eat(&token::Eq) {
+ let eq_span = self.prev_span;
+ let mut is_interpolated_expr = false;
+ if let token::Interpolated(nt) = &self.token.kind {
+ if let token::NtExpr(..) = **nt {
+ is_interpolated_expr = true;
+ }
+ }
+ let token_tree = if is_interpolated_expr {
+ // We need to accept arbitrary interpolated expressions to continue
+ // supporting things like `doc = $expr` that work on stable.
+ // Non-literal interpolated expressions are rejected after expansion.
+ self.parse_token_tree()
+ } else {
+ self.parse_unsuffixed_lit()?.token_tree()
+ };
+
+ MacArgs::Eq(eq_span, token_tree.into())
+ } else {
+ MacArgs::Empty
+ }
+ } else {
+ return self.unexpected();
+ })
}
fn parse_or_use_outer_attributes(
(None, self.parse_path(PathStyle::Expr)?)
};
match self.token.kind {
- token::Not if qself.is_none() => self.parse_pat_mac_invoc(lo, path)?,
+ token::Not if qself.is_none() => self.parse_pat_mac_invoc(path)?,
token::DotDotDot | token::DotDotEq | token::DotDot => {
self.parse_pat_range_starting_with_path(lo, qself, path)?
}
}
/// Parse macro invocation
- fn parse_pat_mac_invoc(&mut self, lo: Span, path: Path) -> PResult<'a, PatKind> {
+ fn parse_pat_mac_invoc(&mut self, path: Path) -> PResult<'a, PatKind> {
self.bump();
- let (delim, tts) = self.expect_delimited_token_tree()?;
+ let args = self.parse_mac_args()?;
let mac = Mac {
path,
- tts,
- delim,
- span: lo.to(self.prev_span),
+ args,
prior_type_ascription: self.last_type_ascription,
};
Ok(PatKind::Mac(mac))
use crate::maybe_whole;
use syntax::ast::{self, QSelf, Path, PathSegment, Ident, ParenthesizedArgs, AngleBracketedArgs};
use syntax::ast::{AnonConst, GenericArg, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode};
+use syntax::ast::MacArgs;
use syntax::ThinVec;
use syntax::token::{self, Token};
use syntax::source_map::{Span, BytePos};
fn parse_path_allowing_meta(&mut self, style: PathStyle) -> PResult<'a, Path> {
let meta_ident = match self.token.kind {
token::Interpolated(ref nt) => match **nt {
- token::NtMeta(ref item) => match item.tokens.is_empty() {
- true => Some(item.path.clone()),
- false => None,
+ token::NtMeta(ref item) => match item.args {
+ MacArgs::Empty => Some(item.path.clone()),
+ _ => None,
},
_ => None,
},
use syntax::ptr::P;
use syntax::ast;
use syntax::ast::{DUMMY_NODE_ID, Stmt, StmtKind, Local, Block, BlockCheckMode, Expr, ExprKind};
-use syntax::ast::{Attribute, AttrStyle, VisibilityKind, MacStmtStyle, Mac, MacDelimiter};
+use syntax::ast::{Attribute, AttrStyle, VisibilityKind, MacStmtStyle, Mac};
use syntax::util::classify;
use syntax::token;
use syntax::source_map::{respan, Span};
}));
}
- let (delim, tts) = self.expect_delimited_token_tree()?;
+ let args = self.parse_mac_args()?;
+ let delim = args.delim();
let hi = self.prev_span;
- let style = if delim == MacDelimiter::Brace {
+ let style = if delim == token::Brace {
MacStmtStyle::Braces
} else {
MacStmtStyle::NoBraces
let mac = Mac {
path,
- tts,
- delim,
- span: lo.to(hi),
+ args,
prior_type_ascription: self.last_type_ascription,
};
- let kind = if delim == MacDelimiter::Brace ||
+ let kind = if delim == token::Brace ||
self.token == token::Semi || self.token == token::Eof {
StmtKind::Mac(P((mac, style, attrs.into())))
}
self.warn_missing_semicolon();
StmtKind::Mac(P((mac, style, attrs.into())))
} else {
- let e = self.mk_expr(mac.span, ExprKind::Mac(mac), ThinVec::new());
+ let e = self.mk_expr(lo.to(hi), ExprKind::Mac(mac), ThinVec::new());
let e = self.maybe_recover_from_bad_qpath(e, true)?;
let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
let path = self.parse_path(PathStyle::Type)?;
if self.eat(&token::Not) {
// Macro invocation in type position
- let (delim, tts) = self.expect_delimited_token_tree()?;
+ let args = self.parse_mac_args()?;
let mac = Mac {
path,
- tts,
- delim,
- span: lo.to(self.prev_span),
+ args,
prior_type_ascription: self.last_type_ascription,
};
TyKind::Mac(mac)
use errors::{PResult, Applicability};
use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP};
-use syntax::ast::{self, Attribute, AttrKind, Ident, MetaItem, MetaItemKind};
+use syntax::ast::{self, Attribute, AttrKind, Ident, MacArgs, MetaItem, MetaItemKind};
use syntax::attr::mk_name_value_item_str;
use syntax::early_buffered_lints::BufferedEarlyLintId;
-use syntax::token;
-use syntax::tokenstream::TokenTree;
use syntax::sess::ParseSess;
use syntax_pos::{Symbol, sym};
// `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
Some((name, _, template, _)) if name != sym::rustc_dummy =>
check_builtin_attribute(sess, attr, name, template),
- _ => if let Some(TokenTree::Token(token)) = attr.get_normal_item().tokens.trees().next() {
- if token == token::Eq {
- // All key-value attributes are restricted to meta-item syntax.
- parse_meta(sess, attr).map_err(|mut err| err.emit()).ok();
- }
+ _ => if let MacArgs::Eq(..) = attr.get_normal_item().args {
+ // All key-value attributes are restricted to meta-item syntax.
+ parse_meta(sess, attr).map_err(|mut err| err.emit()).ok();
}
}
}
|this| visit::walk_enum_def(this, enum_definition, generics, item_id))
}
- fn visit_mac(&mut self, mac: &Mac) {
- // when a new macro kind is added but the author forgets to set it up for expansion
- // because that's the only part that won't cause a compiler error
- self.session.diagnostic()
- .span_bug(mac.span, "macro invocation missed in expansion; did you forget to override \
- the relevant `fold_*()` method in `PlaceholderExpander`?");
- }
-
fn visit_impl_item(&mut self, ii: &'a ImplItem) {
if let ImplItemKind::Method(ref sig, _) = ii.kind {
self.check_fn_decl(&sig.decl);
#![feature(nll)]
-#![recursion_limit="256"]
+use rustc::lint::LintStore;
-pub use registry::Registry;
-
-pub mod registry;
-pub mod load;
pub mod build;
+pub mod load;
+
+/// Structure used to register plugins.
+///
+/// A plugin registrar function takes an `&mut Registry` and should call
+/// methods to register its plugins.
+pub struct Registry<'a> {
+ /// The `LintStore` allows plugins to register new lints.
+ pub lint_store: &'a mut LintStore,
+}
use rustc::middle::cstore::MetadataLoader;
use rustc::session::Session;
use rustc_metadata::locator;
-use crate::registry::Registry;
+use crate::Registry;
use std::borrow::ToOwned;
use std::env;
use std::mem;
use std::path::PathBuf;
-use syntax::ast;
+use syntax::ast::{Crate, Ident};
use syntax::struct_span_err;
-use syntax::symbol::{Symbol, kw, sym};
-use syntax_pos::{Span, DUMMY_SP};
+use syntax::symbol::sym;
+use syntax_pos::Span;
use rustc_error_codes::*;
/// Pointer to a registrar function.
-pub type PluginRegistrarFun =
- fn(&mut Registry<'_>);
-
-pub struct PluginRegistrar {
- pub fun: PluginRegistrarFun,
- pub args: Vec<ast::NestedMetaItem>,
-}
-
-struct PluginLoader<'a> {
- sess: &'a Session,
- metadata_loader: &'a dyn MetadataLoader,
- plugins: Vec<PluginRegistrar>,
-}
+type PluginRegistrarFn = fn(&mut Registry<'_>);
fn call_malformed_plugin_attribute(sess: &Session, span: Span) {
struct_span_err!(sess, span, E0498, "malformed `plugin` attribute")
/// Read plugin metadata and dynamically load registrar functions.
pub fn load_plugins(sess: &Session,
metadata_loader: &dyn MetadataLoader,
- krate: &ast::Crate,
- addl_plugins: Option<Vec<String>>) -> Vec<PluginRegistrar> {
- let mut loader = PluginLoader { sess, metadata_loader, plugins: Vec::new() };
-
- // do not report any error now. since crate attributes are
- // not touched by expansion, every use of plugin without
- // the feature enabled will result in an error later...
- if sess.features_untracked().plugin {
- for attr in &krate.attrs {
- if !attr.check_name(sym::plugin) {
- continue;
- }
+ krate: &Crate) -> Vec<PluginRegistrarFn> {
+ let mut plugins = Vec::new();
- let plugins = match attr.meta_item_list() {
- Some(xs) => xs,
- None => continue,
- };
-
- for plugin in plugins {
- // plugins must have a name and can't be key = value
- let name = plugin.name_or_empty();
- if name != kw::Invalid && !plugin.is_value_str() {
- let args = plugin.meta_item_list().map(ToOwned::to_owned);
- loader.load_plugin(plugin.span(), name, args.unwrap_or_default());
- } else {
- call_malformed_plugin_attribute(sess, attr.span);
- }
- }
+ for attr in &krate.attrs {
+ if !attr.check_name(sym::plugin) {
+ continue;
}
- }
- if let Some(plugins) = addl_plugins {
- for plugin in plugins {
- loader.load_plugin(DUMMY_SP, Symbol::intern(&plugin), vec![]);
+ for plugin in attr.meta_item_list().unwrap_or_default() {
+ match plugin.ident() {
+ Some(ident) if plugin.is_word() =>
+ load_plugin(&mut plugins, sess, metadata_loader, ident),
+ _ => call_malformed_plugin_attribute(sess, plugin.span()),
+ }
}
}
- loader.plugins
+ plugins
}
-impl<'a> PluginLoader<'a> {
- fn load_plugin(&mut self, span: Span, name: Symbol, args: Vec<ast::NestedMetaItem>) {
- let registrar = locator::find_plugin_registrar(self.sess, self.metadata_loader, span, name);
-
- if let Some((lib, disambiguator)) = registrar {
- let symbol = self.sess.generate_plugin_registrar_symbol(disambiguator);
- let fun = self.dylink_registrar(span, lib, symbol);
- self.plugins.push(PluginRegistrar {
- fun,
- args,
- });
- }
+fn load_plugin(plugins: &mut Vec<PluginRegistrarFn>,
+ sess: &Session,
+ metadata_loader: &dyn MetadataLoader,
+ ident: Ident) {
+ let registrar = locator::find_plugin_registrar(sess, metadata_loader, ident.span, ident.name);
+
+ if let Some((lib, disambiguator)) = registrar {
+ let symbol = sess.generate_plugin_registrar_symbol(disambiguator);
+ let fun = dylink_registrar(sess, ident.span, lib, symbol);
+ plugins.push(fun);
}
+}
- // Dynamically link a registrar function into the compiler process.
- fn dylink_registrar(&mut self,
- span: Span,
- path: PathBuf,
- symbol: String) -> PluginRegistrarFun {
- use rustc_metadata::dynamic_lib::DynamicLibrary;
-
- // Make sure the path contains a / or the linker will search for it.
- let path = env::current_dir().unwrap().join(&path);
-
- let lib = match DynamicLibrary::open(Some(&path)) {
- Ok(lib) => lib,
- // this is fatal: there are almost certainly macros we need
- // inside this crate, so continue would spew "macro undefined"
- // errors
- Err(err) => {
- self.sess.span_fatal(span, &err)
- }
- };
-
- unsafe {
- let registrar =
- match lib.symbol(&symbol) {
- Ok(registrar) => {
- mem::transmute::<*mut u8,PluginRegistrarFun>(registrar)
- }
- // again fatal if we can't register macros
- Err(err) => {
- self.sess.span_fatal(span, &err)
- }
- };
-
- // Intentionally leak the dynamic library. We can't ever unload it
- // since the library can make things that will live arbitrarily long
- // (e.g., an @-box cycle or a thread).
- mem::forget(lib);
-
- registrar
+// Dynamically link a registrar function into the compiler process.
+fn dylink_registrar(sess: &Session,
+ span: Span,
+ path: PathBuf,
+ symbol: String) -> PluginRegistrarFn {
+ use rustc_metadata::dynamic_lib::DynamicLibrary;
+
+ // Make sure the path contains a / or the linker will search for it.
+ let path = env::current_dir().unwrap().join(&path);
+
+ let lib = match DynamicLibrary::open(Some(&path)) {
+ Ok(lib) => lib,
+ // this is fatal: there are almost certainly macros we need
+ // inside this crate, so continue would spew "macro undefined"
+ // errors
+ Err(err) => {
+ sess.span_fatal(span, &err)
}
+ };
+
+ unsafe {
+ let registrar =
+ match lib.symbol(&symbol) {
+ Ok(registrar) => {
+ mem::transmute::<*mut u8, PluginRegistrarFn>(registrar)
+ }
+ // again fatal if we can't register macros
+ Err(err) => {
+ sess.span_fatal(span, &err)
+ }
+ };
+
+ // Intentionally leak the dynamic library. We can't ever unload it
+ // since the library can make things that will live arbitrarily long
+ // (e.g., an @-box cycle or a thread).
+ mem::forget(lib);
+
+ registrar
}
}
+++ /dev/null
-//! Used by plugin crates to tell `rustc` about the plugins they provide.
-
-use rustc::lint::LintStore;
-use rustc::session::Session;
-use syntax::ast;
-use syntax_pos::Span;
-
-use std::borrow::ToOwned;
-
-/// Structure used to register plugins.
-///
-/// A plugin registrar function takes an `&mut Registry` and should call
-/// methods to register its plugins.
-///
-/// This struct has public fields and other methods for use by `rustc`
-/// itself. They are not documented here, and plugin authors should
-/// not use them.
-pub struct Registry<'a> {
- /// Compiler session. Useful if you want to emit diagnostic messages
- /// from the plugin registrar.
- pub sess: &'a Session,
-
- /// The `LintStore` allows plugins to register new lints.
- pub lint_store: &'a mut LintStore,
-
- #[doc(hidden)]
- pub args_hidden: Option<Vec<ast::NestedMetaItem>>,
-
- #[doc(hidden)]
- pub krate_span: Span,
-
- #[doc(hidden)]
- pub llvm_passes: Vec<String>,
-}
-
-impl<'a> Registry<'a> {
- #[doc(hidden)]
- pub fn new(sess: &'a Session, lint_store: &'a mut LintStore, krate_span: Span) -> Registry<'a> {
- Registry {
- sess,
- lint_store,
- args_hidden: None,
- krate_span,
- llvm_passes: vec![],
- }
- }
-
- /// Gets the plugin's arguments, if any.
- ///
- /// These are specified inside the `plugin` crate attribute as
- ///
- /// ```no_run
- /// #![plugin(my_plugin_name(... args ...))]
- /// ```
- ///
- /// Returns empty slice in case the plugin was loaded
- /// with `--extra-plugins`
- pub fn args(&self) -> &[ast::NestedMetaItem] {
- self.args_hidden.as_ref().map(|v| &v[..]).unwrap_or(&[])
- }
-
- /// Register an LLVM pass.
- ///
- /// Registration with LLVM itself is handled through static C++ objects with
- /// constructors. This method simply adds a name to the list of passes to
- /// execute.
- pub fn register_llvm_pass(&mut self, name: &str) {
- self.llvm_passes.push(name.to_owned());
- }
-}
}
}
- fn visit_mac(&mut self, mac: &'l ast::Mac) {
- // These shouldn't exist in the AST at this point, log a span bug.
- span_bug!(
- mac.span,
- "macro invocation should have been expanded out of AST"
- );
- }
-
fn visit_pat(&mut self, p: &'l ast::Pat) {
self.process_macro_use(p.span);
self.process_pat(p);
match cx.enter_resolver(|r| r.cstore().load_macro_untracked(did, cx.sess())) {
LoadedMacro::MacroDef(def, _) => {
let matchers: hir::HirVec<Span> = if let ast::ItemKind::MacroDef(ref def) = def.kind {
- let tts: Vec<_> = def.stream().into_trees().collect();
+ let tts: Vec<_> = def.body.inner_tokens().into_trees().collect();
tts.chunks(4).map(|arm| arm[0].span()).collect()
} else {
unreachable!()
//
/// Control flow based on pattern matching.
///
-/// The documentation for this keyword is [not yet complete]. Pull requests welcome!
+/// `match` can be used to run code conditionally. Every pattern must
+/// be handled exhaustively either explicitly or by using wildcards like
+/// `_` in the `match`. Since `match` is an expression, values can also be
+/// returned.
///
-/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601
+/// ```rust
+/// let opt = Option::None::<usize>;
+/// let x = match opt {
+/// Some(int) => int,
+/// None => 10,
+/// };
+/// assert_eq!(x, 10);
+///
+/// let a_number = Option::Some(10);
+/// match a_number {
+/// Some(x) if x <= 5 => println!("0 to 5 num = {}", x),
+/// Some(x @ 6..=10) => println!("6 to 10 num = {}", x),
+/// None => panic!(),
+/// // all other numbers
+/// _ => panic!(),
+/// }
+/// ```
+///
+/// `match` can be used to gain access to the inner members of an enum
+/// and use them directly.
+///
+/// ```rust
+/// enum Outer {
+/// Double(Option<u8>, Option<String>),
+/// Single(Option<u8>),
+/// Empty
+/// }
+///
+/// let get_inner = Outer::Double(None, Some(String::new()));
+/// match get_inner {
+/// Outer::Double(None, Some(st)) => println!("{}", st),
+/// Outer::Single(opt) => println!("{:?}", opt),
+/// _ => panic!(),
+/// }
+/// ```
+///
+/// For more information on `match` and matching in general, see the [Reference].
+///
+/// [Reference]: ../reference/expressions/match-expr.html
mod match_keyword { }
#[doc(keyword = "mod")]
//
/// Capture a [closure]'s environment by value.
///
-/// The documentation for this keyword is [not yet complete]. Pull requests welcome!
+/// `move` converts any variables captured by reference or mutable reference
+/// to owned by value variables. The three [`Fn` trait]'s mirror the ways to capture
+/// variables, when `move` is used, the closures is represented by the `FnOnce` trait.
///
-/// [closure]: ../book/second-edition/ch13-01-closures.html
-/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601
+/// ```rust
+/// let capture = "hello";
+/// let closure = move || {
+/// println!("rust says {}", capture);
+/// };
+/// ```
+///
+/// `move` is often used when [threads] are involved.
+///
+/// ```rust
+/// let x = 5;
+///
+/// std::thread::spawn(move || {
+/// println!("captured {} by value", x)
+/// }).join().unwrap();
+///
+/// // x is no longer available
+/// ```
+///
+/// For more information on the `move` keyword, see the [closure]'s section
+/// of the Rust book or the [threads] section
+///
+/// [`Fn` trait]: ../std/ops/trait.Fn.html
+/// [closure]: ../book/ch13-01-closures.html
+/// [threads]: ../book/ch16-01-threads.html#using-move-closures-with-threads
mod move_keyword { }
#[doc(keyword = "mut")]
//! use std::fs::File;
//! use std::os::unix::prelude::*;
//!
-//! fn main() {
-//! let f = File::create("foo.txt").unwrap();
+//! fn main() -> std::io::Result<()> {
+//! let f = File::create("foo.txt")?;
//! let fd = f.as_raw_fd();
//!
//! // use fd with native unix bindings
+//!
+//! Ok(())
//! }
//! ```
/// ```no_run
/// use std::os::unix::net::UnixListener;
///
- /// let socket = UnixListener::bind("/tmp/sock").unwrap();
- /// let addr = socket.local_addr().expect("Couldn't get local address");
- /// assert_eq!(addr.is_unnamed(), false);
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixListener::bind("/tmp/sock")?;
+ /// let addr = socket.local_addr().expect("Couldn't get local address");
+ /// assert_eq!(addr.is_unnamed(), false);
+ /// Ok(())
+ /// }
/// ```
///
/// An unnamed address:
/// ```
/// use std::os::unix::net::UnixDatagram;
///
- /// let socket = UnixDatagram::unbound().unwrap();
- /// let addr = socket.local_addr().expect("Couldn't get local address");
- /// assert_eq!(addr.is_unnamed(), true);
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixDatagram::unbound()?;
+ /// let addr = socket.local_addr().expect("Couldn't get local address");
+ /// assert_eq!(addr.is_unnamed(), true);
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn is_unnamed(&self) -> bool {
/// use std::os::unix::net::UnixListener;
/// use std::path::Path;
///
- /// let socket = UnixListener::bind("/tmp/sock").unwrap();
- /// let addr = socket.local_addr().expect("Couldn't get local address");
- /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock")));
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixListener::bind("/tmp/sock")?;
+ /// let addr = socket.local_addr().expect("Couldn't get local address");
+ /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock")));
+ /// Ok(())
+ /// }
/// ```
///
/// Without a pathname:
/// ```
/// use std::os::unix::net::UnixDatagram;
///
- /// let socket = UnixDatagram::unbound().unwrap();
- /// let addr = socket.local_addr().expect("Couldn't get local address");
- /// assert_eq!(addr.as_pathname(), None);
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixDatagram::unbound()?;
+ /// let addr = socket.local_addr().expect("Couldn't get local address");
+ /// assert_eq!(addr.as_pathname(), None);
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn as_pathname(&self) -> Option<&Path> {
/// use std::os::unix::net::UnixStream;
/// use std::io::prelude::*;
///
-/// let mut stream = UnixStream::connect("/path/to/my/socket").unwrap();
-/// stream.write_all(b"hello world").unwrap();
-/// let mut response = String::new();
-/// stream.read_to_string(&mut response).unwrap();
-/// println!("{}", response);
+/// fn main() -> std::io::Result<()> {
+/// let mut stream = UnixStream::connect("/path/to/my/socket")?;
+/// stream.write_all(b"hello world")?;
+/// let mut response = String::new();
+/// stream.read_to_string(&mut response)?;
+/// println!("{}", response);
+/// Ok(())
+/// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub struct UnixStream(Socket);
/// ```no_run
/// use std::os::unix::net::UnixStream;
///
- /// let socket = UnixStream::connect("/tmp/sock").unwrap();
- /// let sock_copy = socket.try_clone().expect("Couldn't clone socket");
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixStream::connect("/tmp/sock")?;
+ /// let sock_copy = socket.try_clone().expect("Couldn't clone socket");
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn try_clone(&self) -> io::Result<UnixStream> {
/// ```no_run
/// use std::os::unix::net::UnixStream;
///
- /// let socket = UnixStream::connect("/tmp/sock").unwrap();
- /// let addr = socket.local_addr().expect("Couldn't get local address");
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixStream::connect("/tmp/sock")?;
+ /// let addr = socket.local_addr().expect("Couldn't get local address");
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn local_addr(&self) -> io::Result<SocketAddr> {
/// ```no_run
/// use std::os::unix::net::UnixStream;
///
- /// let socket = UnixStream::connect("/tmp/sock").unwrap();
- /// let addr = socket.peer_addr().expect("Couldn't get peer address");
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixStream::connect("/tmp/sock")?;
+ /// let addr = socket.peer_addr().expect("Couldn't get peer address");
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
/// use std::os::unix::net::UnixStream;
/// use std::time::Duration;
///
- /// let socket = UnixStream::connect("/tmp/sock").unwrap();
- /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout");
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixStream::connect("/tmp/sock")?;
+ /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout");
+ /// Ok(())
+ /// }
/// ```
///
/// An [`Err`] is returned if the zero [`Duration`] is passed to this
/// use std::os::unix::net::UnixStream;
/// use std::time::Duration;
///
- /// let socket = UnixStream::connect("/tmp/sock").unwrap();
- /// let result = socket.set_read_timeout(Some(Duration::new(0, 0)));
- /// let err = result.unwrap_err();
- /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput)
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixStream::connect("/tmp/sock")?;
+ /// let result = socket.set_read_timeout(Some(Duration::new(0, 0)));
+ /// let err = result.unwrap_err();
+ /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
/// use std::os::unix::net::UnixStream;
/// use std::time::Duration;
///
- /// let socket = UnixStream::connect("/tmp/sock").unwrap();
- /// socket.set_write_timeout(Some(Duration::new(1, 0))).expect("Couldn't set write timeout");
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixStream::connect("/tmp/sock")?;
+ /// socket.set_write_timeout(Some(Duration::new(1, 0)))
+ /// .expect("Couldn't set write timeout");
+ /// Ok(())
+ /// }
/// ```
///
/// An [`Err`] is returned if the zero [`Duration`] is passed to this
/// use std::net::UdpSocket;
/// use std::time::Duration;
///
- /// let socket = UdpSocket::bind("127.0.0.1:34254").unwrap();
- /// let result = socket.set_write_timeout(Some(Duration::new(0, 0)));
- /// let err = result.unwrap_err();
- /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput)
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UdpSocket::bind("127.0.0.1:34254")?;
+ /// let result = socket.set_write_timeout(Some(Duration::new(0, 0)));
+ /// let err = result.unwrap_err();
+ /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
/// use std::os::unix::net::UnixStream;
/// use std::time::Duration;
///
- /// let socket = UnixStream::connect("/tmp/sock").unwrap();
- /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout");
- /// assert_eq!(socket.read_timeout().unwrap(), Some(Duration::new(1, 0)));
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixStream::connect("/tmp/sock")?;
+ /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout");
+ /// assert_eq!(socket.read_timeout()?, Some(Duration::new(1, 0)));
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
/// use std::os::unix::net::UnixStream;
/// use std::time::Duration;
///
- /// let socket = UnixStream::connect("/tmp/sock").unwrap();
- /// socket.set_write_timeout(Some(Duration::new(1, 0))).expect("Couldn't set write timeout");
- /// assert_eq!(socket.write_timeout().unwrap(), Some(Duration::new(1, 0)));
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixStream::connect("/tmp/sock")?;
+ /// socket.set_write_timeout(Some(Duration::new(1, 0)))
+ /// .expect("Couldn't set write timeout");
+ /// assert_eq!(socket.write_timeout()?, Some(Duration::new(1, 0)));
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
/// ```no_run
/// use std::os::unix::net::UnixStream;
///
- /// let socket = UnixStream::connect("/tmp/sock").unwrap();
- /// socket.set_nonblocking(true).expect("Couldn't set nonblocking");
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixStream::connect("/tmp/sock")?;
+ /// socket.set_nonblocking(true).expect("Couldn't set nonblocking");
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
/// ```no_run
/// use std::os::unix::net::UnixStream;
///
- /// let socket = UnixStream::connect("/tmp/sock").unwrap();
- /// if let Ok(Some(err)) = socket.take_error() {
- /// println!("Got error: {:?}", err);
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixStream::connect("/tmp/sock")?;
+ /// if let Ok(Some(err)) = socket.take_error() {
+ /// println!("Got error: {:?}", err);
+ /// }
+ /// Ok(())
/// }
/// ```
///
/// use std::os::unix::net::UnixStream;
/// use std::net::Shutdown;
///
- /// let socket = UnixStream::connect("/tmp/sock").unwrap();
- /// socket.shutdown(Shutdown::Both).expect("shutdown function failed");
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixStream::connect("/tmp/sock")?;
+ /// socket.shutdown(Shutdown::Both).expect("shutdown function failed");
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
/// // ...
/// }
///
-/// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
+/// fn main() -> std::io::Result<()> {
+/// let listener = UnixListener::bind("/path/to/the/socket")?;
///
-/// // accept connections and process them, spawning a new thread for each one
-/// for stream in listener.incoming() {
-/// match stream {
-/// Ok(stream) => {
-/// /* connection succeeded */
-/// thread::spawn(|| handle_client(stream));
-/// }
-/// Err(err) => {
-/// /* connection failed */
-/// break;
+/// // accept connections and process them, spawning a new thread for each one
+/// for stream in listener.incoming() {
+/// match stream {
+/// Ok(stream) => {
+/// /* connection succeeded */
+/// thread::spawn(|| handle_client(stream));
+/// }
+/// Err(err) => {
+/// /* connection failed */
+/// break;
+/// }
/// }
/// }
+/// Ok(())
/// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
/// ```no_run
/// use std::os::unix::net::UnixListener;
///
- /// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
+ /// fn main() -> std::io::Result<()> {
+ /// let listener = UnixListener::bind("/path/to/the/socket")?;
///
- /// match listener.accept() {
- /// Ok((socket, addr)) => println!("Got a client: {:?}", addr),
- /// Err(e) => println!("accept function failed: {:?}", e),
+ /// match listener.accept() {
+ /// Ok((socket, addr)) => println!("Got a client: {:?}", addr),
+ /// Err(e) => println!("accept function failed: {:?}", e),
+ /// }
+ /// Ok(())
/// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
/// ```no_run
/// use std::os::unix::net::UnixListener;
///
- /// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
- ///
- /// let listener_copy = listener.try_clone().expect("try_clone failed");
+ /// fn main() -> std::io::Result<()> {
+ /// let listener = UnixListener::bind("/path/to/the/socket")?;
+ /// let listener_copy = listener.try_clone().expect("try_clone failed");
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn try_clone(&self) -> io::Result<UnixListener> {
/// ```no_run
/// use std::os::unix::net::UnixListener;
///
- /// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
- ///
- /// let addr = listener.local_addr().expect("Couldn't get local address");
+ /// fn main() -> std::io::Result<()> {
+ /// let listener = UnixListener::bind("/path/to/the/socket")?;
+ /// let addr = listener.local_addr().expect("Couldn't get local address");
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn local_addr(&self) -> io::Result<SocketAddr> {
/// ```no_run
/// use std::os::unix::net::UnixListener;
///
- /// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
- ///
- /// listener.set_nonblocking(true).expect("Couldn't set non blocking");
+ /// fn main() -> std::io::Result<()> {
+ /// let listener = UnixListener::bind("/path/to/the/socket")?;
+ /// listener.set_nonblocking(true).expect("Couldn't set non blocking");
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
/// ```no_run
/// use std::os::unix::net::UnixListener;
///
- /// let listener = UnixListener::bind("/tmp/sock").unwrap();
+ /// fn main() -> std::io::Result<()> {
+ /// let listener = UnixListener::bind("/tmp/sock")?;
///
- /// if let Ok(Some(err)) = listener.take_error() {
- /// println!("Got error: {:?}", err);
+ /// if let Ok(Some(err)) = listener.take_error() {
+ /// println!("Got error: {:?}", err);
+ /// }
+ /// Ok(())
/// }
/// ```
///
/// // ...
/// }
///
- /// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
- ///
- /// for stream in listener.incoming() {
- /// match stream {
- /// Ok(stream) => {
- /// thread::spawn(|| handle_client(stream));
- /// }
- /// Err(err) => {
- /// break;
+ /// fn main() -> std::io::Result<()> {
+ /// let listener = UnixListener::bind("/path/to/the/socket")?;
+ ///
+ /// for stream in listener.incoming() {
+ /// match stream {
+ /// Ok(stream) => {
+ /// thread::spawn(|| handle_client(stream));
+ /// }
+ /// Err(err) => {
+ /// break;
+ /// }
/// }
/// }
+ /// Ok(())
/// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
/// // ...
/// }
///
-/// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
+/// fn main() -> std::io::Result<()> {
+/// let listener = UnixListener::bind("/path/to/the/socket")?;
///
-/// for stream in listener.incoming() {
-/// match stream {
-/// Ok(stream) => {
-/// thread::spawn(|| handle_client(stream));
-/// }
-/// Err(err) => {
-/// break;
+/// for stream in listener.incoming() {
+/// match stream {
+/// Ok(stream) => {
+/// thread::spawn(|| handle_client(stream));
+/// }
+/// Err(err) => {
+/// break;
+/// }
/// }
/// }
+/// Ok(())
/// }
/// ```
#[derive(Debug)]
/// ```no_run
/// use std::os::unix::net::UnixDatagram;
///
-/// let socket = UnixDatagram::bind("/path/to/my/socket").unwrap();
-/// socket.send_to(b"hello world", "/path/to/other/socket").unwrap();
-/// let mut buf = [0; 100];
-/// let (count, address) = socket.recv_from(&mut buf).unwrap();
-/// println!("socket {:?} sent {:?}", address, &buf[..count]);
+/// fn main() -> std::io::Result<()> {
+/// let socket = UnixDatagram::bind("/path/to/my/socket")?;
+/// socket.send_to(b"hello world", "/path/to/other/socket")?;
+/// let mut buf = [0; 100];
+/// let (count, address) = socket.recv_from(&mut buf)?;
+/// println!("socket {:?} sent {:?}", address, &buf[..count]);
+/// Ok(())
+/// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub struct UnixDatagram(Socket);
/// ```no_run
/// use std::os::unix::net::UnixDatagram;
///
- /// let sock = UnixDatagram::unbound().unwrap();
- /// match sock.connect("/path/to/the/socket") {
- /// Ok(sock) => sock,
- /// Err(e) => {
- /// println!("Couldn't connect: {:?}", e);
- /// return
- /// }
- /// };
+ /// fn main() -> std::io::Result<()> {
+ /// let sock = UnixDatagram::unbound()?;
+ /// match sock.connect("/path/to/the/socket") {
+ /// Ok(sock) => sock,
+ /// Err(e) => {
+ /// println!("Couldn't connect: {:?}", e);
+ /// return Err(e)
+ /// }
+ /// };
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn connect<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
/// ```no_run
/// use std::os::unix::net::UnixDatagram;
///
- /// let sock = UnixDatagram::bind("/path/to/the/socket").unwrap();
- ///
- /// let sock_copy = sock.try_clone().expect("try_clone failed");
+ /// fn main() -> std::io::Result<()> {
+ /// let sock = UnixDatagram::bind("/path/to/the/socket")?;
+ /// let sock_copy = sock.try_clone().expect("try_clone failed");
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn try_clone(&self) -> io::Result<UnixDatagram> {
/// ```no_run
/// use std::os::unix::net::UnixDatagram;
///
- /// let sock = UnixDatagram::bind("/path/to/the/socket").unwrap();
- ///
- /// let addr = sock.local_addr().expect("Couldn't get local address");
+ /// fn main() -> std::io::Result<()> {
+ /// let sock = UnixDatagram::bind("/path/to/the/socket")?;
+ /// let addr = sock.local_addr().expect("Couldn't get local address");
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn local_addr(&self) -> io::Result<SocketAddr> {
/// ```no_run
/// use std::os::unix::net::UnixDatagram;
///
- /// let sock = UnixDatagram::unbound().unwrap();
- /// sock.connect("/path/to/the/socket").unwrap();
+ /// fn main() -> std::io::Result<()> {
+ /// let sock = UnixDatagram::unbound()?;
+ /// sock.connect("/path/to/the/socket")?;
///
- /// let addr = sock.peer_addr().expect("Couldn't get peer address");
+ /// let addr = sock.peer_addr().expect("Couldn't get peer address");
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
/// ```no_run
/// use std::os::unix::net::UnixDatagram;
///
- /// let sock = UnixDatagram::unbound().unwrap();
- /// let mut buf = vec![0; 10];
- /// match sock.recv_from(buf.as_mut_slice()) {
- /// Ok((size, sender)) => println!("received {} bytes from {:?}", size, sender),
- /// Err(e) => println!("recv_from function failed: {:?}", e),
+ /// fn main() -> std::io::Result<()> {
+ /// let sock = UnixDatagram::unbound()?;
+ /// let mut buf = vec![0; 10];
+ /// let (size, sender) = sock.recv_from(buf.as_mut_slice())?;
+ /// println!("received {} bytes from {:?}", size, sender);
+ /// Ok(())
/// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
/// ```no_run
/// use std::os::unix::net::UnixDatagram;
///
- /// let sock = UnixDatagram::bind("/path/to/the/socket").unwrap();
- /// let mut buf = vec![0; 10];
- /// sock.recv(buf.as_mut_slice()).expect("recv function failed");
+ /// fn main() -> std::io::Result<()> {
+ /// let sock = UnixDatagram::bind("/path/to/the/socket")?;
+ /// let mut buf = vec![0; 10];
+ /// sock.recv(buf.as_mut_slice()).expect("recv function failed");
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
/// ```no_run
/// use std::os::unix::net::UnixDatagram;
///
- /// let sock = UnixDatagram::unbound().unwrap();
- /// sock.send_to(b"omelette au fromage", "/some/sock").expect("send_to function failed");
+ /// fn main() -> std::io::Result<()> {
+ /// let sock = UnixDatagram::unbound()?;
+ /// sock.send_to(b"omelette au fromage", "/some/sock").expect("send_to function failed");
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn send_to<P: AsRef<Path>>(&self, buf: &[u8], path: P) -> io::Result<usize> {
/// ```no_run
/// use std::os::unix::net::UnixDatagram;
///
- /// let sock = UnixDatagram::unbound().unwrap();
- /// sock.connect("/some/sock").expect("Couldn't connect");
- /// sock.send(b"omelette au fromage").expect("send_to function failed");
+ /// fn main() -> std::io::Result<()> {
+ /// let sock = UnixDatagram::unbound()?;
+ /// sock.connect("/some/sock").expect("Couldn't connect");
+ /// sock.send(b"omelette au fromage").expect("send_to function failed");
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
/// use std::os::unix::net::UnixDatagram;
/// use std::time::Duration;
///
- /// let sock = UnixDatagram::unbound().unwrap();
- /// sock.set_read_timeout(Some(Duration::new(1, 0))).expect("set_read_timeout function failed");
+ /// fn main() -> std::io::Result<()> {
+ /// let sock = UnixDatagram::unbound()?;
+ /// sock.set_read_timeout(Some(Duration::new(1, 0)))
+ /// .expect("set_read_timeout function failed");
+ /// Ok(())
+ /// }
/// ```
///
/// An [`Err`] is returned if the zero [`Duration`] is passed to this
/// use std::os::unix::net::UnixDatagram;
/// use std::time::Duration;
///
- /// let socket = UnixDatagram::unbound().unwrap();
- /// let result = socket.set_read_timeout(Some(Duration::new(0, 0)));
- /// let err = result.unwrap_err();
- /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput)
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixDatagram::unbound()?;
+ /// let result = socket.set_read_timeout(Some(Duration::new(0, 0)));
+ /// let err = result.unwrap_err();
+ /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
/// use std::os::unix::net::UnixDatagram;
/// use std::time::Duration;
///
- /// let sock = UnixDatagram::unbound().unwrap();
- /// sock.set_write_timeout(Some(Duration::new(1, 0)))
- /// .expect("set_write_timeout function failed");
+ /// fn main() -> std::io::Result<()> {
+ /// let sock = UnixDatagram::unbound()?;
+ /// sock.set_write_timeout(Some(Duration::new(1, 0)))
+ /// .expect("set_write_timeout function failed");
+ /// Ok(())
+ /// }
/// ```
///
/// An [`Err`] is returned if the zero [`Duration`] is passed to this
/// use std::os::unix::net::UnixDatagram;
/// use std::time::Duration;
///
- /// let socket = UnixDatagram::unbound().unwrap();
- /// let result = socket.set_write_timeout(Some(Duration::new(0, 0)));
- /// let err = result.unwrap_err();
- /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput)
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixDatagram::unbound()?;
+ /// let result = socket.set_write_timeout(Some(Duration::new(0, 0)));
+ /// let err = result.unwrap_err();
+ /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
/// use std::os::unix::net::UnixDatagram;
/// use std::time::Duration;
///
- /// let sock = UnixDatagram::unbound().unwrap();
- /// sock.set_read_timeout(Some(Duration::new(1, 0))).expect("set_read_timeout function failed");
- /// assert_eq!(sock.read_timeout().unwrap(), Some(Duration::new(1, 0)));
+ /// fn main() -> std::io::Result<()> {
+ /// let sock = UnixDatagram::unbound()?;
+ /// sock.set_read_timeout(Some(Duration::new(1, 0)))
+ /// .expect("set_read_timeout function failed");
+ /// assert_eq!(sock.read_timeout()?, Some(Duration::new(1, 0)));
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
/// use std::os::unix::net::UnixDatagram;
/// use std::time::Duration;
///
- /// let sock = UnixDatagram::unbound().unwrap();
- /// sock.set_write_timeout(Some(Duration::new(1, 0)))
- /// .expect("set_write_timeout function failed");
- /// assert_eq!(sock.write_timeout().unwrap(), Some(Duration::new(1, 0)));
+ /// fn main() -> std::io::Result<()> {
+ /// let sock = UnixDatagram::unbound()?;
+ /// sock.set_write_timeout(Some(Duration::new(1, 0)))
+ /// .expect("set_write_timeout function failed");
+ /// assert_eq!(sock.write_timeout()?, Some(Duration::new(1, 0)));
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
/// ```
/// use std::os::unix::net::UnixDatagram;
///
- /// let sock = UnixDatagram::unbound().unwrap();
- /// sock.set_nonblocking(true).expect("set_nonblocking function failed");
+ /// fn main() -> std::io::Result<()> {
+ /// let sock = UnixDatagram::unbound()?;
+ /// sock.set_nonblocking(true).expect("set_nonblocking function failed");
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
/// ```no_run
/// use std::os::unix::net::UnixDatagram;
///
- /// let sock = UnixDatagram::unbound().unwrap();
- /// if let Ok(Some(err)) = sock.take_error() {
- /// println!("Got error: {:?}", err);
+ /// fn main() -> std::io::Result<()> {
+ /// let sock = UnixDatagram::unbound()?;
+ /// if let Ok(Some(err)) = sock.take_error() {
+ /// println!("Got error: {:?}", err);
+ /// }
+ /// Ok(())
/// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
/// use std::os::unix::net::UnixDatagram;
/// use std::net::Shutdown;
///
- /// let sock = UnixDatagram::unbound().unwrap();
- /// sock.shutdown(Shutdown::Both).expect("shutdown function failed");
+ /// fn main() -> std::io::Result<()> {
+ /// let sock = UnixDatagram::unbound()?;
+ /// sock.shutdown(Shutdown::Both).expect("shutdown function failed");
+ /// Ok(())
+ /// }
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
use crate::ptr::P;
use crate::source_map::{dummy_spanned, respan, Spanned};
use crate::token::{self, DelimToken};
-use crate::tokenstream::TokenStream;
+use crate::tokenstream::{TokenStream, TokenTree, DelimSpan};
use syntax_pos::symbol::{kw, sym, Symbol};
use syntax_pos::{Span, DUMMY_SP, ExpnId};
use rustc_serialize::{self, Decoder, Encoder};
use rustc_macros::HashStable_Generic;
+use std::iter;
use std::fmt;
#[cfg(test)]
Movable,
}
-/// Represents a macro invocation. The `Path` indicates which macro
-/// is being invoked, and the vector of token-trees contains the source
-/// of the macro invocation.
-///
-/// N.B., the additional ident for a `macro_rules`-style macro is actually
-/// stored in the enclosing item.
+/// Represents a macro invocation. The `path` indicates which macro
+/// is being invoked, and the `args` are arguments passed to it.
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct Mac {
pub path: Path,
- pub delim: MacDelimiter,
- pub tts: TokenStream,
- pub span: Span,
+ pub args: P<MacArgs>,
pub prior_type_ascription: Option<(Span, bool)>,
}
-#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug)]
+impl Mac {
+ pub fn span(&self) -> Span {
+ self.path.span.to(self.args.span().unwrap_or(self.path.span))
+ }
+}
+
+/// Arguments passed to an attribute or a function-like macro.
+#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
+pub enum MacArgs {
+ /// No arguments - `#[attr]`.
+ Empty,
+ /// Delimited arguments - `#[attr()/[]/{}]` or `mac!()/[]/{}`.
+ Delimited(DelimSpan, MacDelimiter, TokenStream),
+ /// Arguments of a key-value attribute - `#[attr = "value"]`.
+ Eq(
+ /// Span of the `=` token.
+ Span,
+ /// Token stream of the "value".
+ TokenStream,
+ ),
+}
+
+impl MacArgs {
+ pub fn delim(&self) -> DelimToken {
+ match self {
+ MacArgs::Delimited(_, delim, _) => delim.to_token(),
+ MacArgs::Empty | MacArgs::Eq(..) => token::NoDelim,
+ }
+ }
+
+ pub fn span(&self) -> Option<Span> {
+ match *self {
+ MacArgs::Empty => None,
+ MacArgs::Delimited(dspan, ..) => Some(dspan.entire()),
+ MacArgs::Eq(eq_span, ref tokens) => Some(eq_span.to(tokens.span().unwrap_or(eq_span))),
+ }
+ }
+
+ /// Tokens inside the delimiters or after `=`.
+ /// Proc macros see these tokens, for example.
+ pub fn inner_tokens(&self) -> TokenStream {
+ match self {
+ MacArgs::Empty => TokenStream::default(),
+ MacArgs::Delimited(.., tokens) |
+ MacArgs::Eq(.., tokens) => tokens.clone(),
+ }
+ }
+
+ /// Tokens together with the delimiters or `=`.
+ /// Use of this method generally means that something suboptimal or hacky is happening.
+ pub fn outer_tokens(&self) -> TokenStream {
+ match *self {
+ MacArgs::Empty => TokenStream::default(),
+ MacArgs::Delimited(dspan, delim, ref tokens) =>
+ TokenTree::Delimited(dspan, delim.to_token(), tokens.clone()).into(),
+ MacArgs::Eq(eq_span, ref tokens) => iter::once(TokenTree::token(token::Eq, eq_span))
+ .chain(tokens.trees()).collect(),
+ }
+ }
+
+ /// Whether a macro with these arguments needs a semicolon
+ /// when used as a standalone item or statement.
+ pub fn need_semicolon(&self) -> bool {
+ !matches!(self, MacArgs::Delimited(_, MacDelimiter::Brace ,_))
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
pub enum MacDelimiter {
Parenthesis,
Bracket,
Brace,
}
-impl Mac {
- pub fn stream(&self) -> TokenStream {
- self.tts.clone()
- }
-}
-
impl MacDelimiter {
crate fn to_token(self) -> DelimToken {
match self {
MacDelimiter::Brace => DelimToken::Brace,
}
}
+
+ pub fn from_token(delim: DelimToken) -> Option<MacDelimiter> {
+ match delim {
+ token::Paren => Some(MacDelimiter::Parenthesis),
+ token::Bracket => Some(MacDelimiter::Bracket),
+ token::Brace => Some(MacDelimiter::Brace),
+ token::NoDelim => None,
+ }
+ }
}
/// Represents a macro definition.
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct MacroDef {
- pub tokens: TokenStream,
+ pub body: P<MacArgs>,
/// `true` if macro was defined with `macro_rules`.
pub legacy: bool,
}
-impl MacroDef {
- pub fn stream(&self) -> TokenStream {
- self.tokens.clone().into()
- }
-}
-
// Clippy uses Hash and PartialEq
#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Copy, Hash, PartialEq, HashStable_Generic)]
pub enum StrStyle {
#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
pub struct AttrItem {
pub path: Path,
- pub tokens: TokenStream,
+ pub args: MacArgs,
}
/// Metadata associated with an item.
use crate::ast;
use crate::ast::{AttrItem, AttrId, AttrKind, AttrStyle, Name, Ident, Path, PathSegment};
-use crate::ast::{MetaItem, MetaItemKind, NestedMetaItem};
+use crate::ast::{MacArgs, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem};
use crate::ast::{Lit, LitKind, Expr, Item, Local, Stmt, StmtKind, GenericParam};
use crate::mut_visit::visit_clobber;
use crate::source_map::{BytePos, Spanned};
pub fn is_word(&self) -> bool {
if let AttrKind::Normal(item) = &self.kind {
- item.tokens.is_empty()
+ matches!(item.args, MacArgs::Empty)
} else {
false
}
impl AttrItem {
pub fn meta(&self, span: Span) -> Option<MetaItem> {
- let mut tokens = self.tokens.trees().peekable();
Some(MetaItem {
path: self.path.clone(),
- kind: if let Some(kind) = MetaItemKind::from_tokens(&mut tokens) {
- if tokens.peek().is_some() {
- return None;
- }
- kind
- } else {
- return None;
- },
+ kind: MetaItemKind::from_mac_args(&self.args)?,
span,
})
}
AttrId(id)
}
-pub fn mk_attr(style: AttrStyle, path: Path, tokens: TokenStream, span: Span) -> Attribute {
- mk_attr_from_item(style, AttrItem { path, tokens }, span)
+pub fn mk_attr(style: AttrStyle, path: Path, args: MacArgs, span: Span) -> Attribute {
+ mk_attr_from_item(style, AttrItem { path, args }, span)
}
pub fn mk_attr_from_item(style: AttrStyle, item: AttrItem, span: Span) -> Attribute {
/// Returns an inner attribute with the given value and span.
pub fn mk_attr_inner(item: MetaItem) -> Attribute {
- mk_attr(AttrStyle::Inner, item.path, item.kind.tokens(item.span), item.span)
+ mk_attr(AttrStyle::Inner, item.path, item.kind.mac_args(item.span), item.span)
}
/// Returns an outer attribute with the given value and span.
pub fn mk_attr_outer(item: MetaItem) -> Attribute {
- mk_attr(AttrStyle::Outer, item.path, item.kind.tokens(item.span), item.span)
+ mk_attr(AttrStyle::Outer, item.path, item.kind.mac_args(item.span), item.span)
}
pub fn mk_doc_comment(style: AttrStyle, comment: Symbol, span: Span) -> Attribute {
}
impl MetaItemKind {
- pub fn token_trees_and_joints(&self, span: Span) -> Vec<TreeAndJoint> {
+ pub fn mac_args(&self, span: Span) -> MacArgs {
+ match self {
+ MetaItemKind::Word => MacArgs::Empty,
+ MetaItemKind::NameValue(lit) => MacArgs::Eq(span, lit.token_tree().into()),
+ MetaItemKind::List(list) => {
+ let mut tts = Vec::new();
+ for (i, item) in list.iter().enumerate() {
+ if i > 0 {
+ tts.push(TokenTree::token(token::Comma, span).into());
+ }
+ tts.extend(item.token_trees_and_joints())
+ }
+ MacArgs::Delimited(
+ DelimSpan::from_single(span), MacDelimiter::Parenthesis, TokenStream::new(tts)
+ )
+ }
+ }
+ }
+
+ fn token_trees_and_joints(&self, span: Span) -> Vec<TreeAndJoint> {
match *self {
MetaItemKind::Word => vec![],
MetaItemKind::NameValue(ref lit) => {
}
}
- // Premature conversions of `TokenTree`s to `TokenStream`s can hurt
- // performance. Do not use this function if `token_trees_and_joints()` can
- // be used instead.
- pub fn tokens(&self, span: Span) -> TokenStream {
- TokenStream::new(self.token_trees_and_joints(span))
- }
-
- fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItemKind>
- where I: Iterator<Item = TokenTree>,
- {
- let delimited = match tokens.peek().cloned() {
- Some(TokenTree::Token(token)) if token == token::Eq => {
- tokens.next();
- return if let Some(TokenTree::Token(token)) = tokens.next() {
- Lit::from_token(&token).ok().map(MetaItemKind::NameValue)
- } else {
- None
- };
- }
- Some(TokenTree::Delimited(_, delim, ref tts)) if delim == token::Paren => {
- tokens.next();
- tts.clone()
- }
- _ => return Some(MetaItemKind::Word),
- };
-
- let mut tokens = delimited.into_trees().peekable();
+ fn list_from_tokens(tokens: TokenStream) -> Option<MetaItemKind> {
+ let mut tokens = tokens.into_trees().peekable();
let mut result = Vec::new();
while let Some(..) = tokens.peek() {
let item = NestedMetaItem::from_tokens(&mut tokens)?;
}
Some(MetaItemKind::List(result))
}
+
+ fn name_value_from_tokens(
+ tokens: &mut impl Iterator<Item = TokenTree>,
+ ) -> Option<MetaItemKind> {
+ match tokens.next() {
+ Some(TokenTree::Token(token)) =>
+ Lit::from_token(&token).ok().map(MetaItemKind::NameValue),
+ _ => None,
+ }
+ }
+
+ fn from_mac_args(args: &MacArgs) -> Option<MetaItemKind> {
+ match args {
+ MacArgs::Delimited(_, MacDelimiter::Parenthesis, tokens) =>
+ MetaItemKind::list_from_tokens(tokens.clone()),
+ MacArgs::Delimited(..) => None,
+ MacArgs::Eq(_, tokens) => {
+ assert!(tokens.len() == 1);
+ MetaItemKind::name_value_from_tokens(&mut tokens.trees())
+ }
+ MacArgs::Empty => Some(MetaItemKind::Word),
+ }
+ }
+
+ fn from_tokens(
+ tokens: &mut iter::Peekable<impl Iterator<Item = TokenTree>>,
+ ) -> Option<MetaItemKind> {
+ match tokens.peek() {
+ Some(TokenTree::Delimited(_, token::Paren, inner_tokens)) => {
+ let inner_tokens = inner_tokens.clone();
+ tokens.next();
+ MetaItemKind::list_from_tokens(inner_tokens)
+ }
+ Some(TokenTree::Delimited(..)) => None,
+ Some(TokenTree::Token(Token { kind: token::Eq, .. })) => {
+ tokens.next();
+ MetaItemKind::name_value_from_tokens(tokens)
+ }
+ _ => Some(MetaItemKind::Word),
+ }
+ }
}
impl NestedMetaItem {
#![feature(const_transmute)]
#![feature(crate_visibility_modifier)]
#![feature(label_break_value)]
+#![feature(matches_macro)]
#![feature(nll)]
#![feature(try_trait)]
#![feature(slice_patterns)]
vis.visit_fn_decl(decl);
}
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+pub fn visit_mac_args<T: MutVisitor>(args: &mut MacArgs, vis: &mut T) {
+ match args {
+ MacArgs::Empty => {}
+ MacArgs::Delimited(dspan, _delim, tokens) => {
+ visit_delim_span(dspan, vis);
+ vis.visit_tts(tokens);
+ }
+ MacArgs::Eq(eq_span, tokens) => {
+ vis.visit_span(eq_span);
+ vis.visit_tts(tokens);
+ }
+ }
+}
+
+pub fn visit_delim_span<T: MutVisitor>(dspan: &mut DelimSpan, vis: &mut T) {
+ vis.visit_span(&mut dspan.open);
+ vis.visit_span(&mut dspan.close);
+}
+
pub fn noop_flat_map_field_pattern<T: MutVisitor>(
mut fp: FieldPat,
vis: &mut T,
pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
let Attribute { kind, id: _, style: _, span } = attr;
match kind {
- AttrKind::Normal(AttrItem { path, tokens }) => {
+ AttrKind::Normal(AttrItem { path, args }) => {
vis.visit_path(path);
- vis.visit_tts(tokens);
+ visit_mac_args(args, vis);
}
AttrKind::DocComment(_) => {}
}
}
pub fn noop_visit_mac<T: MutVisitor>(mac: &mut Mac, vis: &mut T) {
- let Mac { path, delim: _, tts, span, prior_type_ascription: _ } = mac;
+ let Mac { path, args, prior_type_ascription: _ } = mac;
vis.visit_path(path);
- vis.visit_tts(tts);
- vis.visit_span(span);
+ visit_mac_args(args, vis);
}
pub fn noop_visit_macro_def<T: MutVisitor>(macro_def: &mut MacroDef, vis: &mut T) {
- let MacroDef { tokens, legacy: _ } = macro_def;
- vis.visit_tts(tokens);
+ let MacroDef { body, legacy: _ } = macro_def;
+ visit_mac_args(body, vis);
}
pub fn noop_visit_meta_list_item<T: MutVisitor>(li: &mut NestedMetaItem, vis: &mut T) {
token::NtIdent(ident, _is_raw) => vis.visit_ident(ident),
token::NtLifetime(ident) => vis.visit_ident(ident),
token::NtLiteral(expr) => vis.visit_expr(expr),
- token::NtMeta(AttrItem { path, tokens }) => {
+ token::NtMeta(AttrItem { path, args }) => {
vis.visit_path(path);
- vis.visit_tts(tokens);
+ visit_mac_args(args, vis);
}
token::NtPath(path) => vis.visit_path(path),
token::NtTT(tt) => vis.visit_tt(tt),
use crate::ast::{self, BlockCheckMode, PatKind, RangeEnd, RangeSyntax};
use crate::ast::{SelfKind, GenericBound, TraitBoundModifier};
-use crate::ast::{Attribute, MacDelimiter, GenericArg};
+use crate::ast::{Attribute, GenericArg, MacArgs};
use crate::util::parser::{self, AssocOp, Fixity};
use crate::util::comments;
use crate::attr;
fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) {
self.ibox(0);
- match item.tokens.trees().next() {
- Some(TokenTree::Delimited(_, delim, tts)) => {
- self.print_mac_common(
- Some(MacHeader::Path(&item.path)), false, None, delim, tts, true, span
- );
- }
- tree => {
+ match &item.args {
+ MacArgs::Delimited(_, delim, tokens) => self.print_mac_common(
+ Some(MacHeader::Path(&item.path)),
+ false,
+ None,
+ delim.to_token(),
+ tokens.clone(),
+ true,
+ span,
+ ),
+ MacArgs::Empty | MacArgs::Eq(..) => {
self.print_path(&item.path, false, 0);
- if tree.is_some() {
+ if let MacArgs::Eq(_, tokens) = &item.args {
self.space();
- self.print_tts(item.tokens.clone(), true);
+ self.word_space("=");
+ self.print_tts(tokens.clone(), true);
}
}
}
}
ast::ForeignItemKind::Macro(ref m) => {
self.print_mac(m);
- match m.delim {
- MacDelimiter::Brace => {},
- _ => self.s.word(";")
+ if m.args.need_semicolon() {
+ self.s.word(";");
}
}
}
}
ast::ItemKind::Mac(ref mac) => {
self.print_mac(mac);
- match mac.delim {
- MacDelimiter::Brace => {}
- _ => self.s.word(";"),
+ if mac.args.need_semicolon() {
+ self.s.word(";");
}
}
ast::ItemKind::MacroDef(ref macro_def) => {
Some(MacHeader::Keyword(kw)),
has_bang,
Some(item.ident),
- DelimToken::Brace,
- macro_def.stream(),
+ macro_def.body.delim(),
+ macro_def.body.inner_tokens(),
true,
item.span,
);
}
ast::TraitItemKind::Macro(ref mac) => {
self.print_mac(mac);
- match mac.delim {
- MacDelimiter::Brace => {}
- _ => self.s.word(";"),
+ if mac.args.need_semicolon() {
+ self.s.word(";");
}
}
}
}
ast::ImplItemKind::Macro(ref mac) => {
self.print_mac(mac);
- match mac.delim {
- MacDelimiter::Brace => {}
- _ => self.s.word(";"),
+ if mac.args.need_semicolon() {
+ self.s.word(";");
}
}
}
Some(MacHeader::Path(&m.path)),
true,
None,
- m.delim.to_token(),
- m.stream(),
+ m.args.delim(),
+ m.args.inner_tokens(),
true,
- m.span,
+ m.span(),
);
}
self.0.len()
}
+ pub fn span(&self) -> Option<Span> {
+ match &**self.0 {
+ [] => None,
+ [(tt, _)] => Some(tt.span()),
+ [(tt_start, _), .., (tt_end, _)] => Some(tt_start.span().to(tt_end.span())),
+ }
+ }
+
pub fn from_streams(mut streams: SmallVec<[TokenStream; 2]>) -> TokenStream {
match streams.len() {
0 => TokenStream::default(),
pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) {
match attr.kind {
- AttrKind::Normal(ref item) => visitor.visit_tts(item.tokens.clone()),
+ AttrKind::Normal(ref item) => walk_mac_args(visitor, &item.args),
AttrKind::DocComment(_) => {}
}
}
+pub fn walk_mac_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a MacArgs) {
+ match args {
+ MacArgs::Empty => {}
+ MacArgs::Delimited(_dspan, _delim, tokens) => visitor.visit_tts(tokens.clone()),
+ MacArgs::Eq(_eq_span, tokens) => visitor.visit_tts(tokens.clone()),
+ }
+}
+
pub fn walk_tt<'a, V: Visitor<'a>>(visitor: &mut V, tt: TokenTree) {
match tt {
TokenTree::Token(token) => visitor.visit_token(token),
use rustc_parse::parser::Parser;
use rustc_parse::validate_attr;
use syntax::ast::{self, AttrItem, Block, Ident, LitKind, NodeId, PatKind, Path};
-use syntax::ast::{MacStmtStyle, StmtKind, ItemKind};
+use syntax::ast::{MacArgs, MacStmtStyle, StmtKind, ItemKind};
use syntax::attr::{self, HasAttrs, is_builtin_attr};
use syntax::source_map::respan;
use syntax::feature_gate::{self, feature_err};
InvocationKind::Bang { mac, .. } => match ext {
SyntaxExtensionKind::Bang(expander) => {
self.gate_proc_macro_expansion_kind(span, fragment_kind);
- let tok_result = expander.expand(self.cx, span, mac.stream());
+ let tok_result = expander.expand(self.cx, span, mac.args.inner_tokens());
self.parse_ast_fragment(tok_result, fragment_kind, &mac.path, span)
}
SyntaxExtensionKind::LegacyBang(expander) => {
let prev = self.cx.current_expansion.prior_type_ascription;
self.cx.current_expansion.prior_type_ascription = mac.prior_type_ascription;
- let tok_result = expander.expand(self.cx, span, mac.stream());
+ let tok_result = expander.expand(self.cx, span, mac.args.inner_tokens());
let result = if let Some(result) = fragment_kind.make_from(tok_result) {
result
} else {
=> panic!("unexpected annotatable"),
})), DUMMY_SP).into();
let item = attr.unwrap_normal_item();
- let input = self.extract_proc_macro_attr_input(item.tokens, span);
- let tok_result = expander.expand(self.cx, span, input, item_tok);
+ if let MacArgs::Eq(..) = item.args {
+ self.cx.span_err(span, "key-value macro attributes are not supported");
+ }
+ let tok_result =
+ expander.expand(self.cx, span, item.args.inner_tokens(), item_tok);
self.parse_ast_fragment(tok_result, fragment_kind, &item.path, span)
}
SyntaxExtensionKind::LegacyAttr(expander) => {
}
}
- fn extract_proc_macro_attr_input(&self, tokens: TokenStream, span: Span) -> TokenStream {
- let mut trees = tokens.trees();
- match trees.next() {
- Some(TokenTree::Delimited(_, _, tts)) => {
- if trees.next().is_none() {
- return tts.into()
- }
- }
- Some(TokenTree::Token(..)) => {}
- None => return TokenStream::default(),
- }
- self.cx.span_err(span, "custom attribute invocations must be \
- of the form `#[foo]` or `#[foo(..)]`, the macro name must only be \
- followed by a delimiter token");
- TokenStream::default()
- }
-
fn gate_proc_macro_attr_item(&self, span: Span, item: &Annotatable) {
let kind = match item {
Annotatable::Item(item) => match &item.kind {
let meta = attr::mk_list_item(Ident::with_dummy_span(sym::doc), items);
*at = attr::Attribute {
kind: ast::AttrKind::Normal(
- AttrItem { path: meta.path, tokens: meta.kind.tokens(meta.span) },
+ AttrItem { path: meta.path, args: meta.kind.mac_args(meta.span) },
),
span: at.span,
id: at.id,
let tt_spec = ast::Ident::new(sym::tt, def.span);
// Parse the macro_rules! invocation
- let body = match def.kind {
- ast::ItemKind::MacroDef(ref body) => body,
+ let (is_legacy, body) = match &def.kind {
+ ast::ItemKind::MacroDef(macro_def) => (macro_def.legacy, macro_def.body.inner_tokens()),
_ => unreachable!(),
};
mbe::TokenTree::MetaVarDecl(def.span, rhs_nm, tt_spec),
],
separator: Some(Token::new(
- if body.legacy { token::Semi } else { token::Comma },
+ if is_legacy { token::Semi } else { token::Comma },
def.span,
)),
kleene: mbe::KleeneToken::new(mbe::KleeneOp::OneOrMore, def.span),
DelimSpan::dummy(),
Lrc::new(mbe::SequenceRepetition {
tts: vec![mbe::TokenTree::token(
- if body.legacy { token::Semi } else { token::Comma },
+ if is_legacy { token::Semi } else { token::Comma },
def.span,
)],
separator: None,
),
];
- let argument_map = match parse(sess, body.stream(), &argument_gram, None, true) {
+ let argument_map = match parse(sess, body, &argument_gram, None, true) {
Success(m) => m,
Failure(token, msg) => {
let s = parse_failure_msg(&token);
// that is not lint-checked and trigger the "failed to process buffered lint here" bug.
valid &= macro_check::check_meta_variables(sess, ast::CRATE_NODE_ID, def.span, &lhses, &rhses);
- let (transparency, transparency_error) = attr::find_transparency(&def.attrs, body.legacy);
+ let (transparency, transparency_error) = attr::find_transparency(&def.attrs, is_legacy);
match transparency_error {
Some(TransparencyError::UnknownTransparency(value, span)) =>
diag.span_err(span, &format!("unknown macro transparency: `{}`", value)),
}
}
-impl Marker {
- fn visit_delim_span(&mut self, dspan: &mut DelimSpan) {
- self.visit_span(&mut dspan.open);
- self.visit_span(&mut dspan.close);
- }
-}
-
/// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`).
enum Frame {
Delimited { forest: Lrc<mbe::Delimited>, idx: usize, span: DelimSpan },
// jump back out of the Delimited, pop the result_stack and add the new results back to
// the previous results (from outside the Delimited).
mbe::TokenTree::Delimited(mut span, delimited) => {
- marker.visit_delim_span(&mut span);
+ mut_visit::visit_delim_span(&mut span, &mut marker);
stack.push(Frame::Delimited { forest: delimited, idx: 0, span });
result_stack.push(mem::take(&mut result));
}
"foo!( fn main() { body } )".to_string(), &sess).unwrap();
let tts: Vec<_> = match expr.kind {
- ast::ExprKind::Mac(ref mac) => mac.stream().trees().collect(),
+ ast::ExprKind::Mac(ref mac) => mac.args.inner_tokens().trees().collect(),
_ => panic!("not a macro"),
};
use syntax::ast;
use syntax::source_map::{DUMMY_SP, dummy_spanned};
-use syntax::tokenstream::TokenStream;
use syntax::mut_visit::*;
use syntax::ptr::P;
use syntax::ThinVec;
fn mac_placeholder() -> ast::Mac {
ast::Mac {
path: ast::Path { span: DUMMY_SP, segments: Vec::new() },
- tts: TokenStream::default().into(),
- delim: ast::MacDelimiter::Brace,
- span: DUMMY_SP,
+ args: P(ast::MacArgs::Empty),
prior_type_ascription: None,
}
}
use crate::base::{self, *};
use crate::proc_macro_server;
-use syntax::ast::{self, ItemKind};
+use syntax::ast::{self, ItemKind, MacArgs};
use syntax::errors::{Applicability, FatalError};
use syntax::symbol::sym;
use syntax::token;
}
let parse_derive_paths = |attr: &ast::Attribute| {
- if attr.get_normal_item().tokens.is_empty() {
+ if let MacArgs::Empty = attr.get_normal_item().args {
return Ok(Vec::new());
}
rustc_parse::parse_in_attr(cx.parse_sess, attr, |p| p.parse_derive_paths())
use syntax::print::pprust;
use syntax::ptr::P;
use syntax::symbol::{sym, Symbol};
-use syntax::tokenstream::{TokenStream, TokenTree};
+use syntax::tokenstream::{DelimSpan, TokenStream, TokenTree};
use syntax_expand::base::*;
use syntax_pos::{Span, DUMMY_SP};
// `core::panic` and `std::panic` are different macros, so we use call-site
// context to pick up whichever is currently in scope.
let sp = cx.with_call_site_ctxt(sp);
+ let tokens = custom_message.unwrap_or_else(|| {
+ TokenStream::from(TokenTree::token(
+ TokenKind::lit(token::Str, Symbol::intern(&format!(
+ "assertion failed: {}",
+ pprust::expr_to_string(&cond_expr).escape_debug()
+ )), None),
+ DUMMY_SP,
+ ))
+ });
+ let args = P(MacArgs::Delimited(DelimSpan::from_single(sp), MacDelimiter::Parenthesis, tokens));
let panic_call = Mac {
path: Path::from_ident(Ident::new(sym::panic, sp)),
- tts: custom_message.unwrap_or_else(|| {
- TokenStream::from(TokenTree::token(
- TokenKind::lit(token::Str, Symbol::intern(&format!(
- "assertion failed: {}",
- pprust::expr_to_string(&cond_expr).escape_debug()
- )), None),
- DUMMY_SP,
- ))
- }).into(),
- delim: MacDelimiter::Parenthesis,
- span: sp,
+ args,
prior_type_ascription: None,
};
let if_expr = cx.expr_if(
);
let start_span = parser.token.span;
- let AttrItem { path, tokens } = panictry!(parser.parse_attr_item());
+ let AttrItem { path, args } = panictry!(parser.parse_attr_item());
let end_span = parser.token.span;
if parser.token != token::Eof {
parse_sess.span_diagnostic
continue;
}
- krate.attrs.push(mk_attr(AttrStyle::Inner, path, tokens, start_span.to(end_span)));
+ krate.attrs.push(mk_attr(AttrStyle::Inner, path, args, start_span.to(end_span)));
}
krate
fn find_type_parameters(
ty: &ast::Ty,
ty_param_names: &[ast::Name],
- span: Span,
cx: &ExtCtxt<'_>,
) -> Vec<P<ast::Ty>> {
use syntax::visit;
struct Visitor<'a, 'b> {
cx: &'a ExtCtxt<'b>,
- span: Span,
ty_param_names: &'a [ast::Name],
types: Vec<P<ast::Ty>>,
}
}
fn visit_mac(&mut self, mac: &ast::Mac) {
- let span = mac.span.with_ctxt(self.span.ctxt());
- self.cx.span_err(span, "`derive` cannot be used on items with type macros");
+ self.cx.span_err(mac.span(), "`derive` cannot be used on items with type macros");
}
}
- let mut visitor = Visitor {
- ty_param_names,
- types: Vec::new(),
- span,
- cx,
- };
-
+ let mut visitor = Visitor { cx, ty_param_names, types: Vec::new() };
visit::Visitor::visit_ty(&mut visitor, ty);
visitor.types
.collect();
for field_ty in field_tys {
- let tys = find_type_parameters(&field_ty, &ty_param_names, self.span, cx);
+ let tys = find_type_parameters(&field_ty, &ty_param_names, cx);
for ty in tys {
// if we have already handled this type, skip it
cfg_attr,
cfg_attr_multi,
cfg_doctest,
+ cfg_sanitize,
cfg_target_feature,
cfg_target_has_atomic,
cfg_target_thread_local,
Err,
Eq,
Equal,
+ enclosing_scope,
except,
exclusive_range_pattern,
exhaustive_integer_patterns,
rust_eh_unwind_resume,
rust_oom,
rvalue_static_promotion,
+ sanitize,
sanitizer_runtime,
_Self,
self_in_typedefs,
// debug _t => _1;
// debug q => _2;
// let mut _0: i32;
-// let _3: [closure@HirId { owner: DefIndex(4), local_id: 31 }];
-// let mut _4: &[closure@HirId { owner: DefIndex(4), local_id: 31 }];
+// let _3: [closure@foo<T>::{{closure}}#0];
+// let mut _4: &[closure@foo<T>::{{closure}}#0];
// let mut _5: (&i32, &i32);
// let mut _6: &i32;
// let mut _7: &i32;
// }
// bb0: {
// ...
-// _3 = [closure@HirId { owner: DefIndex(4), local_id: 31 }];
+// _3 = [closure@foo::<T>::{{closure}}#0];
// ...
// _4 = &_3;
// ...
// debug t => _1;
// debug q => _2;
// let mut _0: (i32, T);
-// let _3: [closure@HirId { owner: DefIndex(4), local_id: 15 } q:&i32, t:&T];
+// let _3: [closure@foo<T>::{{closure}}#0 q:&i32, t:&T];
// let mut _4: &i32;
// let mut _5: &T;
-// let mut _6: &[closure@HirId { owner: DefIndex(4), local_id: 15 } q:&i32, t:&T];
+// let mut _6: &[closure@foo<T>::{{closure}}#0 q:&i32, t:&T];
// let mut _7: (i32,);
// let mut _8: i32;
// let mut _11: i32;
// _4 = &_2;
// ...
// _5 = &_1;
-// _3 = [closure@HirId { owner: DefIndex(4), local_id: 15 }] { q: move _4, t: move _5 };
+// _3 = [closure@foo::<T>::{{closure}}#0] { q: move _4, t: move _5 };
// ...
// _6 = &_3;
// ...
// debug _t => _1;
// debug q => _2;
// let mut _0: i32;
-// let _3: [closure@HirId { owner: DefIndex(4), local_id: 15 }];
-// let mut _4: &[closure@HirId { owner: DefIndex(4), local_id: 15 }];
+// let _3: [closure@foo<T>::{{closure}}#0];
+// let mut _4: &[closure@foo<T>::{{closure}}#0];
// let mut _5: (i32, i32);
// let mut _6: i32;
// let mut _7: i32;
// }
// bb0: {
// ...
-// _3 = [closure@HirId { owner: DefIndex(4), local_id: 15 }];
+// _3 = [closure@foo::<T>::{{closure}}#0];
// ...
// _4 = &_3;
// ...
// }
// END rustc.main.EraseRegions.after.mir
// START rustc.main-{{closure}}.EraseRegions.after.mir
-// fn main::{{closure}}#0(_1: &[closure@HirId { owner: DefIndex(13), local_id: 72 }], _2: &i32) -> &i32 {
+// fn main::{{closure}}#0(_1: &[closure@main::{{closure}}#0], _2: &i32) -> &i32 {
// ...
// bb0: {
// Retag([fn entry] _1);
+++ /dev/null
-// force-host
-
-#![feature(plugin_registrar)]
-#![feature(rustc_private)]
-
-extern crate rustc;
-extern crate rustc_driver;
-
-use rustc_driver::plugin::Registry;
-
-#[plugin_registrar]
-pub fn plugin_registrar(reg: &mut Registry) {
- // This pass is built in to LLVM.
- //
- // Normally, we would name a pass that was registered through
- // C++ static object constructors in the same .so file as the
- // plugin registrar.
- reg.register_llvm_pass("gvn");
-}
--- /dev/null
+// aux-build:empty-plugin.rs
+// ignore-stage1
+
+#![plugin(empty_plugin)]
+//~^ ERROR compiler plugins are deprecated
+//~| WARN use of deprecated attribute `plugin`: compiler plugins are deprecated
+
+fn main() {}
--- /dev/null
+error[E0658]: compiler plugins are deprecated
+ --> $DIR/feature-gate-plugin.rs:4:1
+ |
+LL | #![plugin(empty_plugin)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/29597
+ = help: add `#![feature(plugin)]` to the crate attributes to enable
+
+warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
+ --> $DIR/feature-gate-plugin.rs:4:1
+ |
+LL | #![plugin(empty_plugin)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
+ |
+ = note: `#[warn(deprecated)]` on by default
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
-// run-pass
+// check-pass
// aux-build:lint-plugin-test.rs
// ignore-stage1
-// compile-flags: -Z extra-plugins=lint_plugin_test
+// compile-flags: -Z crate-attr=plugin(lint_plugin_test)
-#![allow(dead_code)]
+#![feature(plugin)]
fn lintme() { } //~ WARNING item is named 'lintme'
+warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
+ --> <crate attribute>:1:1
+ |
+LL | plugin(lint_plugin_test)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
+ |
+ = note: `#[warn(deprecated)]` on by default
+
warning: item is named 'lintme'
--> $DIR/lint-plugin-cmdline-load.rs:8:1
|
+++ /dev/null
-// run-pass
-// aux-build:llvm-pass-plugin.rs
-// ignore-stage1
-
-#![feature(plugin)]
-#![plugin(llvm_pass_plugin)] //~ WARNING compiler plugins are deprecated
-
-pub fn main() { }
+++ /dev/null
-warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
- --> $DIR/llvm-pass-plugin.rs:6:1
- |
-LL | #![plugin(llvm_pass_plugin)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
- |
- = note: `#[warn(deprecated)]` on by default
-
-// run-pass
-
-#![allow(plugin_as_library)]
-#![allow(dead_code)]
-#![allow(unused_variables)]
-#![allow(unused_imports)]
+// check-pass
// aux-build:macro-crate-test.rs
// ignore-stage1
+++ /dev/null
-// check-pass
-// aux-build:empty-plugin.rs
-// ignore-stage1
-
-#![feature(plugin)]
-#![plugin(empty_plugin)] //~ WARNING compiler plugins are deprecated
-
-fn main() {}
+++ /dev/null
-warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
- --> $DIR/plugin-args-1.rs:6:1
- |
-LL | #![plugin(empty_plugin)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
- |
- = note: `#[warn(deprecated)]` on by default
-
+++ /dev/null
-// check-pass
-// aux-build:empty-plugin.rs
-// ignore-stage1
-
-#![feature(plugin)]
-#![plugin(empty_plugin())] //~ WARNING compiler plugins are deprecated
-
-fn main() {}
+++ /dev/null
-warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
- --> $DIR/plugin-args-2.rs:6:1
- |
-LL | #![plugin(empty_plugin())]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
- |
- = note: `#[warn(deprecated)]` on by default
-
+++ /dev/null
-// check-pass
-// aux-build:empty-plugin.rs
-// ignore-stage1
-
-#![feature(plugin)]
-#![plugin(empty_plugin(hello(there), how(are="you")))] //~ WARNING compiler plugins are deprecated
-
-fn main() {}
+++ /dev/null
-warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
- --> $DIR/plugin-args-3.rs:6:1
- |
-LL | #![plugin(empty_plugin(hello(there), how(are="you")))]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
- |
- = note: `#[warn(deprecated)]` on by default
-
--- /dev/null
+// aux-build:empty-plugin.rs
+// ignore-stage1
+
+#![feature(plugin)]
+#![plugin(empty_plugin(args))]
+//~^ ERROR malformed `plugin` attribute
+//~| WARNING compiler plugins are deprecated
+
+fn main() {}
--- /dev/null
+error[E0498]: malformed `plugin` attribute
+ --> $DIR/plugin-args.rs:5:11
+ |
+LL | #![plugin(empty_plugin(args))]
+ | ^^^^^^^^^^^^^^^^^^ malformed attribute
+
+warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
+ --> $DIR/plugin-args.rs:5:1
+ |
+LL | #![plugin(empty_plugin(args))]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
+ |
+ = note: `#[warn(deprecated)]` on by default
+
+error: aborting due to previous error
+
+// check-pass
// aux-build:empty-plugin.rs
// ignore-cross-compile
//
// empty_plugin will not compile on a cross-compiled target because
// libsyntax is not compiled for it.
-#![deny(plugin_as_library)]
+extern crate empty_plugin; // OK, plugin crates are still crates
-extern crate empty_plugin; //~ ERROR compiler plugin used as an ordinary library
-
-fn main() { }
+fn main() {}
+++ /dev/null
-error: compiler plugin used as an ordinary library
- --> $DIR/plugin-as-extern-crate.rs:9:1
- |
-LL | extern crate empty_plugin;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: lint level defined here
- --> $DIR/plugin-as-extern-crate.rs:7:9
- |
-LL | #![deny(plugin_as_library)]
- | ^^^^^^^^^^^^^^^^^
-
-error: aborting due to previous error
-
error[E0277]: the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> $DIR/try-on-option-in-async.rs:8:9
|
-LL | x?;
- | ^^ cannot use the `?` operator in an async block that returns `{integer}`
+LL | async {
+ | ___________-
+LL | | let x: Option<u32> = None;
+LL | | x?;
+ | | ^^ cannot use the `?` operator in an async block that returns `{integer}`
+LL | | 22
+LL | | }.await
+ | |_____- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `std::ops::Try` is not implemented for `{integer}`
= note: required by `std::ops::Try::from_error`
error[E0277]: the `?` operator can only be used in an async closure that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> $DIR/try-on-option-in-async.rs:16:9
|
-LL | x?;
- | ^^ cannot use the `?` operator in an async closure that returns `u32`
+LL | let async_closure = async || {
+ | __________________________________-
+LL | | let x: Option<u32> = None;
+LL | | x?;
+ | | ^^ cannot use the `?` operator in an async closure that returns `u32`
+LL | | 22_u32
+LL | | };
+ | |_____- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `std::ops::Try` is not implemented for `u32`
= note: required by `std::ops::Try::from_error`
error[E0277]: the `?` operator can only be used in an async function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> $DIR/try-on-option-in-async.rs:25:5
|
-LL | x?;
- | ^^ cannot use the `?` operator in an async function that returns `u32`
+LL | async fn an_async_function() -> u32 {
+ | _____________________________________-
+LL | | let x: Option<u32> = None;
+LL | | x?;
+ | | ^^ cannot use the `?` operator in an async function that returns `u32`
+LL | | 22
+LL | | }
+ | |_- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `std::ops::Try` is not implemented for `u32`
= note: required by `std::ops::Try::from_error`
-const _X: i32 = {
+// Ensure that we point the user to the erroneous borrow but not to any subsequent borrows of that
+// initial one.
+
+const _: i32 = {
let mut a = 5;
- let p = &mut a; //~ ERROR references in constants may only refer to immutable values
+ let p = &mut a; //~ ERROR references in constants may only refer to immutable values
- let reborrow = {p}; //~ ERROR references in constants may only refer to immutable values
+ let reborrow = {p};
let pp = &reborrow;
let ppp = &pp;
***ppp
};
+const _: std::cell::Cell<i32> = {
+ let mut a = std::cell::Cell::new(5);
+ let p = &a; //~ ERROR cannot borrow a constant which may contain interior mutability
+
+ let reborrow = {p};
+ let pp = &reborrow;
+ let ppp = &pp;
+ a
+};
+
fn main() {}
error[E0017]: references in constants may only refer to immutable values
- --> $DIR/const-multi-ref.rs:3:13
+ --> $DIR/const-multi-ref.rs:6:13
|
LL | let p = &mut a;
| ^^^^^^ constants require immutable values
-error[E0017]: references in constants may only refer to immutable values
- --> $DIR/const-multi-ref.rs:5:21
+error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead
+ --> $DIR/const-multi-ref.rs:16:13
|
-LL | let reborrow = {p};
- | ^ constants require immutable values
+LL | let p = &a;
+ | ^^
error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0017`.
+Some errors have detailed explanations: E0017, E0492.
+For more information about an error, try `rustc --explain E0017`.
static X: i32 = 1;
const C: i32 = 2;
+static mut M: i32 = 3;
const CR: &'static mut i32 = &mut C; //~ ERROR E0017
static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0017
+ //~| ERROR E0019
//~| ERROR cannot borrow
static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0017
+static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M }; //~ ERROR E0017
fn main() {}
error[E0017]: references in constants may only refer to immutable values
- --> $DIR/E0017.rs:4:30
+ --> $DIR/E0017.rs:5:30
|
LL | const CR: &'static mut i32 = &mut C;
| ^^^^^^ constants require immutable values
+error[E0019]: static contains unimplemented expression type
+ --> $DIR/E0017.rs:6:39
+ |
+LL | static STATIC_REF: &'static mut i32 = &mut X;
+ | ^^^^^^
+
error[E0017]: references in statics may only refer to immutable values
- --> $DIR/E0017.rs:5:39
+ --> $DIR/E0017.rs:6:39
|
LL | static STATIC_REF: &'static mut i32 = &mut X;
| ^^^^^^ statics require immutable values
error[E0596]: cannot borrow immutable static item `X` as mutable
- --> $DIR/E0017.rs:5:39
+ --> $DIR/E0017.rs:6:39
|
LL | static STATIC_REF: &'static mut i32 = &mut X;
| ^^^^^^ cannot borrow as mutable
error[E0017]: references in statics may only refer to immutable values
- --> $DIR/E0017.rs:7:38
+ --> $DIR/E0017.rs:9:38
|
LL | static CONST_REF: &'static mut i32 = &mut C;
| ^^^^^^ statics require immutable values
-error: aborting due to 4 previous errors
+error[E0017]: references in statics may only refer to immutable values
+ --> $DIR/E0017.rs:10:52
+ |
+LL | static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M };
+ | ^^^^^^ statics require immutable values
+
+error: aborting due to 6 previous errors
-Some errors have detailed explanations: E0017, E0596.
+Some errors have detailed explanations: E0017, E0019, E0596.
For more information about an error, try `rustc --explain E0017`.
+++ /dev/null
-static X: i32 = 1;
-const C: i32 = 2;
-
-const CR: &'static mut i32 = &mut C; //~ ERROR E0017
-static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0017
- //~| ERROR cannot borrow
-static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0017
-
-fn main() {}
+++ /dev/null
-error[E0017]: references in constants may only refer to immutable values
- --> $DIR/E0388.rs:4:30
- |
-LL | const CR: &'static mut i32 = &mut C;
- | ^^^^^^ constants require immutable values
-
-error[E0017]: references in statics may only refer to immutable values
- --> $DIR/E0388.rs:5:39
- |
-LL | static STATIC_REF: &'static mut i32 = &mut X;
- | ^^^^^^ statics require immutable values
-
-error[E0596]: cannot borrow immutable static item `X` as mutable
- --> $DIR/E0388.rs:5:39
- |
-LL | static STATIC_REF: &'static mut i32 = &mut X;
- | ^^^^^^ cannot borrow as mutable
-
-error[E0017]: references in statics may only refer to immutable values
- --> $DIR/E0388.rs:7:38
- |
-LL | static CONST_REF: &'static mut i32 = &mut C;
- | ^^^^^^ statics require immutable values
-
-error: aborting due to 4 previous errors
-
-Some errors have detailed explanations: E0017, E0596.
-For more information about an error, try `rustc --explain E0017`.
--- /dev/null
+#[cfg(not(sanitize = "thread"))]
+//~^ `cfg(sanitize)` is experimental
+fn main() {}
--- /dev/null
+error[E0658]: `cfg(sanitize)` is experimental and subject to change
+ --> $DIR/feature-gate-cfg_sanitize.rs:1:11
+ |
+LL | #[cfg(not(sanitize = "thread"))]
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/39699
+ = help: add `#![feature(cfg_sanitize)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
+++ /dev/null
-// Test that `#![plugin(...)]` attribute is gated by `plugin` feature gate
-
-#![plugin(foo)]
-//~^ ERROR compiler plugins are deprecated
-//~| WARN use of deprecated attribute `plugin`: compiler plugins are deprecated
-
-fn main() {}
+++ /dev/null
-error[E0658]: compiler plugins are deprecated
- --> $DIR/feature-gate-plugin.rs:3:1
- |
-LL | #![plugin(foo)]
- | ^^^^^^^^^^^^^^^
- |
- = note: for more information, see https://github.com/rust-lang/rust/issues/29597
- = help: add `#![feature(plugin)]` to the crate attributes to enable
-
-warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
- --> $DIR/feature-gate-plugin.rs:3:1
- |
-LL | #![plugin(foo)]
- | ^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
- |
- = note: `#[warn(deprecated)]` on by default
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0658`.
pub fn main() {
foo!();
- assert!({one! two()}); //~ ERROR expected open delimiter
+ assert!({one! two()}); //~ ERROR expected one of `(`, `[`, or `{`, found `two`
// regardless of whether nested macro_rules works, the following should at
// least throw a conventional error.
- assert!({one! two}); //~ ERROR expected open delimiter
+ assert!({one! two}); //~ ERROR expected one of `(`, `[`, or `{`, found `two`
}
-error: expected open delimiter
+error: expected one of `(`, `[`, or `{`, found `two`
--> $DIR/issue-10536.rs:14:19
|
LL | assert!({one! two()});
- | ^^^ expected open delimiter
+ | ^^^ expected one of `(`, `[`, or `{`
-error: expected open delimiter
+error: expected one of `(`, `[`, or `{`, found `two`
--> $DIR/issue-10536.rs:18:19
|
LL | assert!({one! two});
- | ^^^ expected open delimiter
+ | ^^^ expected one of `(`, `[`, or `{`
error: aborting due to 2 previous errors
--> $DIR/malformed-plugin-1.rs:2:1
|
LL | #![plugin]
- | ^^^^^^^^^^ help: must be of the form: `#[plugin(name|name(args))]`
+ | ^^^^^^^^^^ help: must be of the form: `#[plugin(name)]`
warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
--> $DIR/malformed-plugin-1.rs:2:1
--> $DIR/malformed-plugin-2.rs:2:1
|
LL | #![plugin="bleh"]
- | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[plugin(name|name(args))]`
+ | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[plugin(name)]`
warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
--> $DIR/malformed-plugin-2.rs:2:1
error[E0498]: malformed `plugin` attribute
- --> $DIR/malformed-plugin-3.rs:2:1
+ --> $DIR/malformed-plugin-3.rs:2:11
|
LL | #![plugin(foo="bleh")]
- | ^^^^^^^^^^^^^^^^^^^^^^ malformed attribute
+ | ^^^^^^^^^^ malformed attribute
warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
--> $DIR/malformed-plugin-3.rs:2:1
|
= note: defining type: DefId(0:4 ~ escape_argument_callee[317d]::test[0]::{{closure}}[0]) with closure substs [
i16,
- for<'r, 's, 't0> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) mut &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) i32, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't0)) i32)),
+ for<'r, 's, 't0> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) mut &ReLateBound(DebruijnIndex(0), BrNamed('s)) i32, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) i32)),
]
error: lifetime may not live long enough
|
= note: defining type: DefId(0:4 ~ escape_argument[317d]::test[0]::{{closure}}[0]) with closure substs [
i16,
- for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) mut &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) i32, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) i32)),
+ for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) mut &ReLateBound(DebruijnIndex(0), BrNamed('s)) i32, &ReLateBound(DebruijnIndex(0), BrNamed('s)) i32)),
]
note: No external requirements
|
= note: defining type: DefId(0:18 ~ propagate_approximated_fail_no_postdom[317d]::supply[0]::{{closure}}[0]) with closure substs [
i16,
- for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) u32>)),
+ for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>)),
]
= note: late-bound region is '_#4r
= note: late-bound region is '_#5r
|
= note: defining type: DefId(0:16 ~ propagate_approximated_ref[317d]::supply[0]::{{closure}}[0]) with closure substs [
i16,
- for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't1)) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't1)) u32>)),
+ for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>)),
]
= note: late-bound region is '_#3r
= note: late-bound region is '_#4r
|
= note: defining type: DefId(0:9 ~ propagate_approximated_shorter_to_static_comparing_against_free[317d]::case1[0]::{{closure}}[0]) with closure substs [
i32,
- for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) u32>)),
+ for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>)),
]
error[E0521]: borrowed data escapes outside of closure
|
= note: defining type: DefId(0:11 ~ propagate_approximated_shorter_to_static_comparing_against_free[317d]::case2[0]::{{closure}}[0]) with closure substs [
i32,
- for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) u32>)),
+ for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>)),
]
= note: number of external vids: 2
= note: where '_#1r: '_#0r
|
= note: defining type: DefId(0:16 ~ propagate_approximated_shorter_to_static_no_bound[317d]::supply[0]::{{closure}}[0]) with closure substs [
i16,
- for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't1)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't2)) u32>)),
+ for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t1)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t2)) u32>)),
]
= note: late-bound region is '_#2r
= note: late-bound region is '_#3r
|
= note: defining type: DefId(0:16 ~ propagate_approximated_shorter_to_static_wrong_bound[317d]::supply[0]::{{closure}}[0]) with closure substs [
i16,
- for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't0)) std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't1)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't1)) u32>)),
+ for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>)),
]
= note: late-bound region is '_#3r
= note: late-bound region is '_#4r
|
= note: defining type: DefId(0:16 ~ propagate_approximated_val[317d]::test[0]::{{closure}}[0]) with closure substs [
i16,
- for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) u32>)),
+ for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>)),
]
= note: late-bound region is '_#3r
= note: late-bound region is '_#4r
|
= note: defining type: DefId(0:14 ~ propagate_despite_same_free_region[317d]::supply[0]::{{closure}}[0]) with closure substs [
i16,
- for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) u32>)),
+ for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>)),
]
= note: late-bound region is '_#3r
= note: number of external vids: 4
|
= note: defining type: DefId(0:16 ~ propagate_fail_to_approximate_longer_no_bounds[317d]::supply[0]::{{closure}}[0]) with closure substs [
i16,
- for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't1)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) u32>)),
+ for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>)),
]
= note: late-bound region is '_#2r
= note: late-bound region is '_#3r
|
= note: defining type: DefId(0:16 ~ propagate_fail_to_approximate_longer_wrong_bounds[317d]::supply[0]::{{closure}}[0]) with closure substs [
i16,
- for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't1)) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 't1)) u32>)),
+ for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>)),
]
= note: late-bound region is '_#3r
= note: late-bound region is '_#4r
|
= note: defining type: DefId(0:4 ~ return_wrong_bound_region[317d]::test[0]::{{closure}}[0]) with closure substs [
i16,
- for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) i32, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) i32)) -> &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) i32,
+ for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) i32, &ReLateBound(DebruijnIndex(0), BrNamed('s)) i32)) -> &ReLateBound(DebruijnIndex(0), BrNamed('r)) i32,
]
error: lifetime may not live long enough
LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: consider adding an explicit lifetime bound `T: ReFree(DefId(0:15 ~ projection_one_region_closure[317d]::no_relationships_late[0]), BrNamed(crate0:DefIndex(16), 'a))`...
+ = help: consider adding an explicit lifetime bound `T: ReFree(DefId(0:15 ~ projection_one_region_closure[317d]::no_relationships_late[0]), BrNamed(DefId(0:16 ~ projection_one_region_closure[317d]::no_relationships_late[0]::'a[0]), 'a))`...
error: lifetime may not live long enough
--> $DIR/projection-one-region-closure.rs:45:39
LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: consider adding an explicit lifetime bound `<T as Anything<'_#5r, '_#6r>>::AssocType: ReFree(DefId(0:17 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_late[0]), BrNamed(crate0:DefIndex(18), 'a))`...
+ = help: consider adding an explicit lifetime bound `<T as Anything<'_#5r, '_#6r>>::AssocType: ReFree(DefId(0:17 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_late[0]), BrNamed(DefId(0:18 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_late[0]::'a[0]), 'a))`...
note: External requirements
--> $DIR/projection-two-region-trait-bound-closure.rs:48:29
= note: defining type: DefId(0:11 ~ ty_param_closure_approximate_lower_bound[317d]::generic[0]::{{closure}}[0]) with closure substs [
T,
i16,
- for<'r, 's> extern "rust-call" fn((std::option::Option<std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) ()>>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) T)),
+ for<'r, 's> extern "rust-call" fn((std::option::Option<std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('r)) ()>>, &ReLateBound(DebruijnIndex(0), BrNamed('s)) T)),
]
= note: number of external vids: 2
= note: where T: '_#1r
= note: defining type: DefId(0:15 ~ ty_param_closure_approximate_lower_bound[317d]::generic_fail[0]::{{closure}}[0]) with closure substs [
T,
i16,
- for<'r, 's> extern "rust-call" fn((std::option::Option<std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 'r)) ()>>, &ReLateBound(DebruijnIndex(0), BrNamed(crate0:DefIndex(0), 's)) T)),
+ for<'r, 's> extern "rust-call" fn((std::option::Option<std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('r)) ()>>, &ReLateBound(DebruijnIndex(0), BrNamed('s)) T)),
]
= note: late-bound region is '_#2r
= note: number of external vids: 3
LL | twice(cell, value, |a, b| invoke(a, b));
| ^^^^^^^^^^^^^^^^^^^
|
- = help: consider adding an explicit lifetime bound `T: ReFree(DefId(0:12 ~ ty_param_closure_approximate_lower_bound[317d]::generic_fail[0]), BrNamed(crate0:DefIndex(13), 'a))`...
+ = help: consider adding an explicit lifetime bound `T: ReFree(DefId(0:12 ~ ty_param_closure_approximate_lower_bound[317d]::generic_fail[0]), BrNamed(DefId(0:13 ~ ty_param_closure_approximate_lower_bound[317d]::generic_fail[0]::'a[0]), 'a))`...
error: aborting due to previous error
LL | | })
| |_____^
|
- = help: consider adding an explicit lifetime bound `T: ReFree(DefId(0:11 ~ ty_param_closure_outlives_from_where_clause[317d]::no_region[0]), BrNamed(crate0:DefIndex(12), 'a))`...
+ = help: consider adding an explicit lifetime bound `T: ReFree(DefId(0:11 ~ ty_param_closure_outlives_from_where_clause[317d]::no_region[0]), BrNamed(DefId(0:12 ~ ty_param_closure_outlives_from_where_clause[317d]::no_region[0]::'a[0]), 'a))`...
note: External requirements
--> $DIR/ty-param-closure-outlives-from-where-clause.rs:43:26
LL | | })
| |_____^
|
- = help: consider adding an explicit lifetime bound `T: ReFree(DefId(0:19 ~ ty_param_closure_outlives_from_where_clause[317d]::wrong_region[0]), BrNamed(crate0:DefIndex(20), 'a))`...
+ = help: consider adding an explicit lifetime bound `T: ReFree(DefId(0:19 ~ ty_param_closure_outlives_from_where_clause[317d]::wrong_region[0]), BrNamed(DefId(0:20 ~ ty_param_closure_outlives_from_where_clause[317d]::wrong_region[0]::'a[0]), 'a))`...
note: External requirements
--> $DIR/ty-param-closure-outlives-from-where-clause.rs:77:26
--- /dev/null
+// Test scope annotations from `enclosing_scope` parameter
+
+#![feature(rustc_attrs)]
+
+#[rustc_on_unimplemented(enclosing_scope="in this scope")]
+trait Trait{}
+
+struct Foo;
+
+fn f<T: Trait>(x: T) {}
+
+fn main() {
+ let x = || {
+ f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied
+ let y = || {
+ f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied
+ };
+ };
+
+ {
+ {
+ f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied
+ }
+ }
+
+ f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied
+}
--- /dev/null
+error[E0277]: the trait bound `Foo: Trait` is not satisfied
+ --> $DIR/enclosing-scope.rs:14:11
+ |
+LL | fn f<T: Trait>(x: T) {}
+ | - ----- required by this bound in `f`
+...
+LL | let x = || {
+ | _____________-
+LL | | f(Foo{});
+ | | ^^^^^ the trait `Trait` is not implemented for `Foo`
+LL | | let y = || {
+LL | | f(Foo{});
+LL | | };
+LL | | };
+ | |_____- in this scope
+
+error[E0277]: the trait bound `Foo: Trait` is not satisfied
+ --> $DIR/enclosing-scope.rs:16:15
+ |
+LL | fn f<T: Trait>(x: T) {}
+ | - ----- required by this bound in `f`
+...
+LL | let y = || {
+ | _________________-
+LL | | f(Foo{});
+ | | ^^^^^ the trait `Trait` is not implemented for `Foo`
+LL | | };
+ | |_________- in this scope
+
+error[E0277]: the trait bound `Foo: Trait` is not satisfied
+ --> $DIR/enclosing-scope.rs:22:15
+ |
+LL | fn f<T: Trait>(x: T) {}
+ | - ----- required by this bound in `f`
+LL |
+LL | / fn main() {
+LL | | let x = || {
+LL | | f(Foo{});
+LL | | let y = || {
+... |
+LL | | f(Foo{});
+ | | ^^^^^ the trait `Trait` is not implemented for `Foo`
+... |
+LL | | f(Foo{});
+LL | | }
+ | |_- in this scope
+
+error[E0277]: the trait bound `Foo: Trait` is not satisfied
+ --> $DIR/enclosing-scope.rs:26:7
+ |
+LL | fn f<T: Trait>(x: T) {}
+ | - ----- required by this bound in `f`
+LL |
+LL | / fn main() {
+LL | | let x = || {
+LL | | f(Foo{});
+LL | | let y = || {
+... |
+LL | | f(Foo{});
+ | | ^^^^^ the trait `Trait` is not implemented for `Foo`
+LL | | }
+ | |_- in this scope
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
fn main() {
- foo! bar < //~ ERROR expected open delimiter
+ foo! bar < //~ ERROR expected one of `(`, `[`, or `{`, found `bar`
}
-error: expected open delimiter
+error: expected one of `(`, `[`, or `{`, found `bar`
--> $DIR/macro-bad-delimiter-ident.rs:2:10
|
LL | foo! bar <
- | ^^^ expected open delimiter
+ | ^^^ expected one of `(`, `[`, or `{`
error: aborting due to previous error
//~| ERROR: non-builtin inner attributes are unstable
}
-#[empty_attr = "y"] //~ ERROR: must only be followed by a delimiter token
+#[empty_attr = "y"] //~ ERROR: key-value macro attributes are not supported
fn _test3() {}
fn attrs() {
= note: for more information, see https://github.com/rust-lang/rust/issues/54727
= help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
-error: custom attribute invocations must be of the form `#[foo]` or `#[foo(..)]`, the macro name must only be followed by a delimiter token
+error: key-value macro attributes are not supported
--> $DIR/proc-macro-gates.rs:21:1
|
LL | #[empty_attr = "y"]
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> $DIR/disallowed-positions.rs:46:8
|
-LL | if (let 0 = 0)? {}
- | ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+LL | / fn nested_within_if_expr() {
+LL | | if &let 0 = 0 {}
+LL | |
+LL | |
+... |
+LL | | if (let 0 = 0)? {}
+ | | ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+... |
+LL | | if let true = let true = true {}
+LL | | }
+ | |_- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `std::ops::Try` is not implemented for `()`
= note: required by `std::ops::Try::from_error`
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> $DIR/disallowed-positions.rs:110:11
|
-LL | while (let 0 = 0)? {}
- | ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+LL | / fn nested_within_while_expr() {
+LL | | while &let 0 = 0 {}
+LL | |
+LL | |
+... |
+LL | | while (let 0 = 0)? {}
+ | | ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+... |
+LL | | while let true = let true = true {}
+LL | | }
+ | |_- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `std::ops::Try` is not implemented for `()`
= note: required by `std::ops::Try::from_error`
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> $DIR/disallowed-positions.rs:183:5
|
-LL | (let 0 = 0)?;
- | ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+LL | / fn outside_if_and_while_expr() {
+LL | | &let 0 = 0;
+LL | |
+LL | | !let 0 = 0;
+... |
+LL | | (let 0 = 0)?;
+ | | ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+... |
+LL | |
+LL | | }
+ | |_- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `std::ops::Try` is not implemented for `()`
= note: required by `std::ops::Try::from_error`
--- /dev/null
+// Verifies that when compiling with -Zsanitizer=option,
+// the `#[cfg(sanitize = "option")]` attribute is configured.
+
+// needs-sanitizer-support
+// only-linux
+// only-x86_64
+// check-pass
+// revisions: address leak memory thread
+//[address]compile-flags: -Zsanitizer=address --cfg address
+//[leak]compile-flags: -Zsanitizer=leak --cfg leak
+//[memory]compile-flags: -Zsanitizer=memory --cfg memory
+//[thread]compile-flags: -Zsanitizer=thread --cfg thread
+
+#![feature(cfg_sanitize)]
+
+#[cfg(all(sanitize = "address", address))]
+fn main() {}
+
+#[cfg(all(sanitize = "leak", leak))]
+fn main() {}
+
+#[cfg(all(sanitize = "memory", memory))]
+fn main() {}
+
+#[cfg(all(sanitize = "thread", thread))]
+fn main() {}
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> $DIR/try-on-option-diagnostics.rs:7:5
|
-LL | x?;
- | ^^ cannot use the `?` operator in a function that returns `u32`
+LL | / fn a_function() -> u32 {
+LL | | let x: Option<u32> = None;
+LL | | x?;
+ | | ^^ cannot use the `?` operator in a function that returns `u32`
+LL | | 22
+LL | | }
+ | |_- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `std::ops::Try` is not implemented for `u32`
= note: required by `std::ops::Try::from_error`
error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> $DIR/try-on-option-diagnostics.rs:14:9
|
-LL | x?;
- | ^^ cannot use the `?` operator in a closure that returns `{integer}`
+LL | let a_closure = || {
+ | _____________________-
+LL | | let x: Option<u32> = None;
+LL | | x?;
+ | | ^^ cannot use the `?` operator in a closure that returns `{integer}`
+LL | | 22
+LL | | };
+ | |_____- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `std::ops::Try` is not implemented for `{integer}`
= note: required by `std::ops::Try::from_error`
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> $DIR/try-on-option.rs:13:5
|
-LL | x?;
- | ^^ cannot use the `?` operator in a function that returns `u32`
+LL | / fn bar() -> u32 {
+LL | | let x: Option<u32> = None;
+LL | | x?;
+ | | ^^ cannot use the `?` operator in a function that returns `u32`
+LL | | 22
+LL | | }
+ | |_- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `std::ops::Try` is not implemented for `u32`
= note: required by `std::ops::Try::from_error`
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> $DIR/try-operator-on-main.rs:9:5
|
-LL | std::fs::File::open("foo")?;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+LL | / fn main() {
+LL | | // error for a `Try` type on a non-`Try` fn
+LL | | std::fs::File::open("foo")?;
+ | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+LL | |
+... |
+LL | | try_trait_generic::<()>();
+LL | | }
+ | |_- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `std::ops::Try` is not implemented for `()`
= note: required by `std::ops::Try::from_error`