#![feature(drain_filter)]
#![feature(exact_size_is_empty)]
#![feature(new_uninit)]
-#![feature(option_flattening)]
#![feature(pattern)]
#![feature(trusted_len)]
#![feature(try_reserve)]
/// This will statically either panic, or do nothing.
pub fn panic_if_uninhabited<T>();
+ /// Gets a reference to a static `Location` indicating where it was called.
+ #[cfg(not(bootstrap))]
+ pub fn caller_location() -> &'static crate::panic::Location<'static>;
+
/// Creates a value initialized to zero.
///
/// `init` is unsafe because it returns a zeroed-out datum,
/// Panics the current thread.
///
/// For details, see `std::macros`.
+#[cfg(bootstrap)]
#[macro_export]
-#[allow_internal_unstable(core_panic)]
+#[allow_internal_unstable(core_panic, panic_internals)]
#[stable(feature = "core", since = "1.6.0")]
macro_rules! panic {
() => (
});
}
+/// Panics the current thread.
+///
+/// For details, see `std::macros`.
+#[cfg(not(bootstrap))]
+#[macro_export]
+#[allow_internal_unstable(core_panic, panic_internals)]
+#[stable(feature = "core", since = "1.6.0")]
+macro_rules! panic {
+ () => (
+ $crate::panic!("explicit panic")
+ );
+ ($msg:expr) => ({
+ const LOC: &$crate::panic::Location<'_> = &$crate::panic::Location::internal_constructor(
+ $crate::file!(),
+ $crate::line!(),
+ $crate::column!(),
+ );
+ $crate::panicking::panic($msg, LOC)
+ });
+ ($msg:expr,) => (
+ $crate::panic!($msg)
+ );
+ ($fmt:expr, $($arg:tt)+) => ({
+ const LOC: &$crate::panic::Location<'_> = &$crate::panic::Location::internal_constructor(
+ $crate::file!(),
+ $crate::line!(),
+ $crate::column!(),
+ );
+ $crate::panicking::panic_fmt($crate::format_args!($fmt, $($arg)+), LOC)
+ });
+}
+
/// Asserts that two expressions are equal to each other (using [`PartialEq`]).
///
/// On panic, this macro will print the values of the expressions with their
/// # Examples
/// Basic usage:
/// ```
- /// #![feature(option_flattening)]
/// let x: Option<Option<u32>> = Some(Some(6));
/// assert_eq!(Some(6), x.flatten());
///
/// ```
/// Flattening once only removes one level of nesting:
/// ```
- /// #![feature(option_flattening)]
/// let x: Option<Option<Option<u32>>> = Some(Some(Some(6)));
/// assert_eq!(Some(Some(6)), x.flatten());
/// assert_eq!(Some(6), x.flatten().flatten());
/// ```
#[inline]
- #[unstable(feature = "option_flattening", issue = "60258")]
+ #[stable(feature = "option_flattening", since = "1.40.0")]
pub fn flatten(self) -> Option<T> {
self.and_then(convert::identity)
}
pub struct PanicInfo<'a> {
payload: &'a (dyn Any + Send),
message: Option<&'a fmt::Arguments<'a>>,
- location: Location<'a>,
+ location: &'a Location<'a>,
}
impl<'a> PanicInfo<'a> {
issue = "0")]
#[doc(hidden)]
#[inline]
- pub fn internal_constructor(message: Option<&'a fmt::Arguments<'a>>,
- location: Location<'a>)
- -> Self {
+ pub fn internal_constructor(
+ message: Option<&'a fmt::Arguments<'a>>,
+ location: &'a Location<'a>,
+ ) -> Self {
struct NoPayload;
- PanicInfo { payload: &NoPayload, location, message }
+ PanicInfo {
+ location,
+ message,
+ payload: &NoPayload,
+ }
}
#[doc(hidden)]
///
/// panic!("Normal panic");
/// ```
+#[cfg_attr(not(bootstrap), lang = "panic_location")]
#[derive(Debug)]
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub struct Location<'a> {
and related macros",
issue = "0")]
#[doc(hidden)]
- pub fn internal_constructor(file: &'a str, line: u32, col: u32) -> Self {
+ pub const fn internal_constructor(file: &'a str, line: u32, col: u32) -> Self {
Location { file, line, col }
}
use crate::fmt;
use crate::panic::{Location, PanicInfo};
+#[cfg(bootstrap)]
#[cold]
// never inline unless panic_immediate_abort to avoid code
// bloat at the call sites as much as possible
panic_fmt(fmt::Arguments::new_v1(&[expr], &[]), &(file, line, col))
}
+#[cfg(not(bootstrap))]
+#[cold]
+// never inline unless panic_immediate_abort to avoid code
+// bloat at the call sites as much as possible
+#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))]
+#[lang = "panic"]
+pub fn panic(expr: &str, location: &Location<'_>) -> ! {
+ if cfg!(feature = "panic_immediate_abort") {
+ unsafe { super::intrinsics::abort() }
+ }
+
+ // Use Arguments::new_v1 instead of format_args!("{}", expr) to potentially
+ // reduce size overhead. The format_args! macro uses str's Display trait to
+ // write expr, which calls Formatter::pad, which must accommodate string
+ // truncation and padding (even though none is used here). Using
+ // Arguments::new_v1 may allow the compiler to omit Formatter::pad from the
+ // output binary, saving up to a few kilobytes.
+ panic_fmt(fmt::Arguments::new_v1(&[expr], &[]), location)
+}
+
+#[cfg(bootstrap)]
#[cold]
#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))]
#[lang = "panic_bounds_check"]
len, index), file_line_col)
}
+#[cfg(not(bootstrap))]
+#[cold]
+#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))]
+#[lang = "panic_bounds_check"]
+fn panic_bounds_check(location: &Location<'_>, index: usize, len: usize) -> ! {
+ if cfg!(feature = "panic_immediate_abort") {
+ unsafe { super::intrinsics::abort() }
+ }
+
+ panic_fmt(
+ format_args!("index out of bounds: the len is {} but the index is {}", len, index),
+ location
+ )
+}
+
+#[cfg(bootstrap)]
#[cold]
#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))]
#[cfg_attr( feature="panic_immediate_abort" ,inline)]
}
let (file, line, col) = *file_line_col;
- let pi = PanicInfo::internal_constructor(
- Some(&fmt),
- Location::internal_constructor(file, line, col),
- );
+ let location = Location::internal_constructor(file, line, col);
+ let pi = PanicInfo::internal_constructor(Some(&fmt), &location);
+ unsafe { panic_impl(&pi) }
+}
+
+#[cfg(not(bootstrap))]
+#[cold]
+#[cfg_attr(not(feature="panic_immediate_abort"),inline(never))]
+#[cfg_attr( feature="panic_immediate_abort" ,inline)]
+pub fn panic_fmt(fmt: fmt::Arguments<'_>, location: &Location<'_>) -> ! {
+ if cfg!(feature = "panic_immediate_abort") {
+ unsafe { super::intrinsics::abort() }
+ }
+
+ // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call
+ extern "Rust" {
+ #[lang = "panic_impl"]
+ fn panic_impl(pi: &PanicInfo<'_>) -> !;
+ }
+
+ let pi = PanicInfo::internal_constructor(Some(&fmt), location);
unsafe { panic_impl(&pi) }
}
PanicFnLangItem, "panic", panic_fn, Target::Fn;
PanicBoundsCheckFnLangItem, "panic_bounds_check", panic_bounds_check_fn, Target::Fn;
PanicInfoLangItem, "panic_info", panic_info, Target::Struct;
+ PanicLocationLangItem, "panic_location", panic_location, Target::Struct;
PanicImplLangItem, "panic_impl", panic_impl, Target::Fn;
// Libstd panic entry point. Necessary for const eval to be able to catch it
BeginPanicFnLangItem, "begin_panic", begin_panic_fn, Target::Fn;
/// Returns the inferred outlives predicates (e.g., for `struct
/// Foo<'a, T> { x: &'a T }`, this would return `T: 'a`).
- query inferred_outlives_of(_: DefId) -> &'tcx [ty::Predicate<'tcx>] {}
+ query inferred_outlives_of(_: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] {}
/// Maps from the `DefId` of a trait to the list of
/// super-predicates. This is a subset of the full list of
no_force
desc { "extract field of const" }
}
+
+ query const_caller_location(key: (syntax_pos::Symbol, u32, u32)) -> &'tcx ty::Const<'tcx> {
+ eval_always
+ no_force
+ desc { "get a &core::panic::Location referring to a span" }
+ }
}
TypeChecking {
// Let Ti be the first such type.
// - No uncovered type parameters P1..=Pn may appear in T0..Ti (excluding Ti)
//
- fn uncover_fundamental_ty(ty: Ty<'_>) -> Vec<Ty<'_>> {
- if fundamental_ty(ty) {
- ty.walk_shallow().flat_map(|ty| uncover_fundamental_ty(ty)).collect()
+ fn uncover_fundamental_ty<'a>(
+ tcx: TyCtxt<'_>,
+ ty: Ty<'a>,
+ in_crate: InCrate,
+ ) -> Vec<Ty<'a>> {
+ if fundamental_ty(ty) && !ty_is_local(tcx, ty, in_crate) {
+ ty.walk_shallow().flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)).collect()
} else {
vec![ty]
}
}
- for input_ty in trait_ref.input_types().flat_map(uncover_fundamental_ty) {
+ for input_ty in
+ trait_ref.input_types().flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate))
+ {
debug!("orphan_check_trait_ref: check ty `{:?}`", input_ty);
if ty_is_local(tcx, input_ty, in_crate) {
debug!("orphan_check_trait_ref: ty_is_local `{:?}`", input_ty);
/// For each struct with outlive bounds, maps to a vector of the
/// predicate of its outlive bounds. If an item has no outlives
/// bounds, it will have no entry.
- pub predicates: FxHashMap<DefId, &'tcx [ty::Predicate<'tcx>]>,
+ pub predicates: FxHashMap<DefId, &'tcx [(ty::Predicate<'tcx>, Span)]>,
}
impl<'tcx> AsRef<Predicate<'tcx>> for Predicate<'tcx> {
DUMMY_SP
}
}
+
+impl Key for (Symbol, u32, u32) {
+ fn query_crate(&self) -> CrateNum {
+ LOCAL_CRATE
+ }
+
+ fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
+ DUMMY_SP
+ }
+}
use std::ops::{Deref, Range};
use std::ptr;
use std::iter::TrustedLen;
-use syntax::symbol::Symbol;
// All Builders must have an llfn associated with them
#[must_use]
// Forward to the `get_static` method of `CodegenCx`
self.cx().get_static(def_id)
}
-
- fn static_panic_msg(
- &mut self,
- msg: Option<Symbol>,
- filename: Symbol,
- line: Self::Value,
- col: Self::Value,
- kind: &str,
- ) -> Self::Value {
- let align = self.tcx.data_layout.aggregate_align.abi
- .max(self.tcx.data_layout.i32_align.abi)
- .max(self.tcx.data_layout.pointer_align.abi);
-
- let filename = self.const_str_slice(filename);
-
- let with_msg_components;
- let without_msg_components;
-
- let components = if let Some(msg) = msg {
- let msg = self.const_str_slice(msg);
- with_msg_components = [msg, filename, line, col];
- &with_msg_components as &[_]
- } else {
- without_msg_components = [filename, line, col];
- &without_msg_components as &[_]
- };
-
- let struct_ = self.const_struct(&components, false);
- self.static_addr_of(struct_, align, Some(kind))
- }
}
impl Builder<'a, 'll, 'tcx> {
//! Code that is useful in various codegen modules.
use crate::llvm::{self, True, False, Bool, BasicBlock, OperandBundleDef, ConstantInt};
-use crate::abi;
use crate::consts;
use crate::type_::Type;
use crate::type_of::LayoutLlvmExt;
}
impl CodegenCx<'ll, 'tcx> {
- pub fn const_fat_ptr(
- &self,
- ptr: &'ll Value,
- meta: &'ll Value
- ) -> &'ll Value {
- assert_eq!(abi::FAT_PTR_ADDR, 0);
- assert_eq!(abi::FAT_PTR_EXTRA, 1);
- self.const_struct(&[ptr, meta], false)
- }
-
pub fn const_array(&self, ty: &'ll Type, elts: &[&'ll Value]) -> &'ll Value {
unsafe {
return llvm::LLVMConstArray(ty, elts.as_ptr(), elts.len() as c_uint);
}
}
- pub fn const_str_slice(&self, s: Symbol) -> &'ll Value {
- let len = s.as_str().len();
- let cs = consts::ptrcast(self.const_cstr(s, false),
- self.type_ptr_to(self.layout_of(self.tcx.mk_str()).llvm_type(self)));
- self.const_fat_ptr(cs, self.const_usize(len as u64))
- }
-
pub fn const_get_elt(&self, v: &'ll Value, idx: u64) -> &'ll Value {
unsafe {
assert_eq!(idx as c_uint as u64, idx);
unsafe { llvm::LLVMConstReal(t, val) }
}
+ fn const_str(&self, s: Symbol) -> (&'ll Value, &'ll Value) {
+ let len = s.as_str().len();
+ let cs = consts::ptrcast(self.const_cstr(s, false),
+ self.type_ptr_to(self.layout_of(self.tcx.mk_str()).llvm_type(self)));
+ (cs, self.const_usize(len as u64))
+ }
+
fn const_struct(
&self,
elts: &[&'ll Value],
use std::borrow::Cow;
-use syntax::symbol::Symbol;
-use syntax_pos::Pos;
+use syntax::{source_map::Span, symbol::Symbol};
use super::{FunctionCx, LocalRef};
use super::place::PlaceRef;
self.set_debug_loc(&mut bx, terminator.source_info);
// Get the location information.
- let loc = bx.sess().source_map().lookup_char_pos(span.lo());
- let filename = Symbol::intern(&loc.file.name.to_string());
- let line = bx.const_u32(loc.line as u32);
- let col = bx.const_u32(loc.col.to_usize() as u32 + 1);
+ let location = self.get_caller_location(&mut bx, span).immediate();
// Put together the arguments to the panic entry point.
let (lang_item, args) = match msg {
PanicInfo::BoundsCheck { ref len, ref index } => {
let len = self.codegen_operand(&mut bx, len).immediate();
let index = self.codegen_operand(&mut bx, index).immediate();
-
- let file_line_col = bx.static_panic_msg(
- None,
- filename,
- line,
- col,
- "panic_bounds_check_loc",
- );
- (lang_items::PanicBoundsCheckFnLangItem,
- vec![file_line_col, index, len])
+ (lang_items::PanicBoundsCheckFnLangItem, vec![location, index, len])
}
_ => {
let msg_str = Symbol::intern(msg.description());
- let msg_file_line_col = bx.static_panic_msg(
- Some(msg_str),
- filename,
- line,
- col,
- "panic_loc",
- );
- (lang_items::PanicFnLangItem,
- vec![msg_file_line_col])
+ let msg = bx.const_str(msg_str);
+ (lang_items::PanicFnLangItem, vec![msg.0, msg.1, location])
}
};
let ty = instance.unwrap().substs.type_at(0);
let layout = bx.layout_of(ty);
if layout.abi.is_uninhabited() {
- let loc = bx.sess().source_map().lookup_char_pos(span.lo());
- let filename = Symbol::intern(&loc.file.name.to_string());
- let line = bx.const_u32(loc.line as u32);
- let col = bx.const_u32(loc.col.to_usize() as u32 + 1);
-
- let str = format!(
- "Attempted to instantiate uninhabited type {}",
- ty
- );
- let msg_str = Symbol::intern(&str);
- let msg_file_line_col = bx.static_panic_msg(
- Some(msg_str),
- filename,
- line,
- col,
- "panic_loc",
- );
+ let msg_str = format!("Attempted to instantiate uninhabited type {}", ty);
+ let msg = bx.const_str(Symbol::intern(&msg_str));
+ let location = self.get_caller_location(&mut bx, span).immediate();
// Obtain the panic entry point.
let def_id =
&mut bx,
fn_ty,
llfn,
- &[msg_file_line_col],
+ &[msg.0, msg.1, location],
destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)),
cleanup,
);
ReturnDest::Nothing
};
+ if intrinsic == Some("caller_location") {
+ if let Some((_, target)) = destination.as_ref() {
+ let location = self.get_caller_location(&mut bx, span);
+
+ if let ReturnDest::IndirectOperand(tmp, _) = ret_dest {
+ location.val.store(&mut bx, tmp);
+ }
+ self.store_return(&mut bx, ret_dest, &fn_ty.ret, location.immediate());
+
+ helper.maybe_sideeffect(self.mir, &mut bx, &[*target]);
+ helper.funclet_br(self, &mut bx, *target);
+ }
+ return;
+ }
+
if intrinsic.is_some() && intrinsic != Some("drop_in_place") {
let dest = match ret_dest {
_ if fn_ty.ret.is_indirect() => llargs[0],
}
}
+ fn get_caller_location(
+ &mut self,
+ bx: &mut Bx,
+ span: Span,
+ ) -> OperandRef<'tcx, Bx::Value> {
+ let caller = bx.tcx().sess.source_map().lookup_char_pos(span.lo());
+ let const_loc = bx.tcx().const_caller_location((
+ Symbol::intern(&caller.file.name.to_string()),
+ caller.line as u32,
+ caller.col_display as u32 + 1,
+ ));
+ OperandRef::from_const(bx, const_loc)
+ }
+
fn get_personality_slot(
&mut self,
bx: &mut Bx
use rustc::mir::interpret::Allocation;
use rustc::mir::interpret::Scalar;
use rustc::ty::layout;
+use syntax_pos::Symbol;
pub trait ConstMethods<'tcx>: BackendTypes {
// Constant constructors
fn const_u8(&self, i: u8) -> Self::Value;
fn const_real(&self, t: Self::Type, val: f64) -> Self::Value;
+ fn const_str(&self, s: Symbol) -> (Self::Value, Self::Value);
fn const_struct(&self, elts: &[Self::Value], packed: bool) -> Self::Value;
fn const_to_opt_uint(&self, v: Self::Value) -> Option<u64>;
use super::BackendTypes;
-use syntax_pos::symbol::Symbol;
use rustc::hir::def_id::DefId;
use rustc::ty::layout::Align;
pub trait StaticBuilderMethods: BackendTypes {
fn get_static(&mut self, def_id: DefId) -> Self::Value;
- fn static_panic_msg(
- &mut self,
- msg: Option<Symbol>,
- filename: Symbol,
- line: Self::Value,
- col: Self::Value,
- kind: &str,
- ) -> Self::Value;
}
pub trait Callbacks {
/// Called before creating the compiler instance
fn config(&mut self, _config: &mut interface::Config) {}
- /// Called early during compilation to allow other drivers to easily register lints.
- fn extra_lints(&mut self, _ls: &mut lint::LintStore) {}
/// Called after parsing. Return value instructs the compiler whether to
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
fn after_parsing(&mut self, _compiler: &interface::Compiler) -> Compilation {
impl ExplicitOutlivesRequirements {
fn lifetimes_outliving_lifetime<'tcx>(
- inferred_outlives: &'tcx [ty::Predicate<'tcx>],
+ inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)],
index: u32,
) -> Vec<ty::Region<'tcx>> {
- inferred_outlives.iter().filter_map(|pred| {
+ inferred_outlives.iter().filter_map(|(pred, _)| {
match pred {
ty::Predicate::RegionOutlives(outlives) => {
let outlives = outlives.skip_binder();
}
fn lifetimes_outliving_type<'tcx>(
- inferred_outlives: &'tcx [ty::Predicate<'tcx>],
+ inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)],
index: u32,
) -> Vec<ty::Region<'tcx>> {
- inferred_outlives.iter().filter_map(|pred| {
+ inferred_outlives.iter().filter_map(|(pred, _)| {
match pred {
ty::Predicate::TypeOutlives(outlives) => {
let outlives = outlives.skip_binder();
&self,
param: &'tcx hir::GenericParam,
tcx: TyCtxt<'tcx>,
- inferred_outlives: &'tcx [ty::Predicate<'tcx>],
+ inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)],
ty_generics: &'tcx ty::Generics,
) -> Vec<ty::Region<'tcx>> {
let index = ty_generics.param_def_id_to_index[
return TAG_INVALID_SPAN.encode(self)
}
+ // HACK(eddyb) there's no way to indicate which crate a Span is coming
+ // from right now, so decoding would fail to find the SourceFile if
+ // it's not local to the crate the Span is found in.
+ if self.source_file_cache.is_imported() {
+ return TAG_INVALID_SPAN.encode(self)
+ }
+
TAG_VALID_SPAN.encode(self)?;
span.lo.encode(self)?;
.filter(|source_file| {
// No need to re-export imported source_files, as any downstream
// crate will import them from their original source.
+ // FIXME(eddyb) the `Span` encoding should take that into account.
!source_file.is_imported()
})
.map(|source_file| {
use rustc::hir::def::DefKind;
use rustc::hir::def_id::DefId;
+use rustc::middle::lang_items::PanicLocationLangItem;
use rustc::mir::interpret::{ConstEvalErr, ErrorHandled, ScalarMaybeUndef};
use rustc::mir;
use rustc::ty::{self, Ty, TyCtxt, subst::Subst};
use rustc_data_structures::fx::FxHashMap;
use crate::interpret::eval_nullary_intrinsic;
-use syntax::source_map::{Span, DUMMY_SP};
+use syntax::{source_map::{Span, DUMMY_SP}, symbol::Symbol};
use crate::interpret::{self,
PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar, Pointer,
ecx.run()?;
// Intern the result
- intern_const_alloc_recursive(
- ecx,
- cid.instance.def_id(),
- ret,
- )?;
+ intern_const_alloc_recursive(ecx, tcx.static_mutability(cid.instance.def_id()), ret)?;
debug!("eval_body_using_ecx done: {:?}", *ret);
Ok(ret)
fn call_intrinsic(
ecx: &mut InterpCx<'mir, 'tcx, Self>,
+ span: Span,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx>],
dest: PlaceTy<'tcx>,
) -> InterpResult<'tcx> {
- if ecx.emulate_intrinsic(instance, args, dest)? {
+ if ecx.emulate_intrinsic(span, instance, args, dest)? {
return Ok(());
}
// An intrinsic that we do not support
op_to_const(&ecx, field)
}
+pub fn const_caller_location<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ (file, line, col): (Symbol, u32, u32),
+) -> &'tcx ty::Const<'tcx> {
+ trace!("const_caller_location: {}:{}:{}", file, line, col);
+ let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all());
+
+ let loc_ty = tcx.mk_imm_ref(
+ tcx.lifetimes.re_static,
+ 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();
+ intern_const_alloc_recursive(&mut ecx, None, loc_place).unwrap();
+ let loc_const = ty::Const {
+ ty: loc_ty,
+ val: ConstValue::Scalar(loc_place.ptr.into()),
+ };
+
+ tcx.mk_const(loc_const)
+}
+
// this function uses `unwrap` copiously, because an already validated constant must have valid
// fields and can thus never fail outside of compiler bugs
pub fn const_variant_index<'tcx>(
use rustc::ty::{Ty, self};
use rustc::mir::interpret::{InterpResult, ErrorHandled};
use rustc::hir;
-use rustc::hir::def_id::DefId;
use super::validity::RefTracking;
use rustc_data_structures::fx::FxHashSet;
pub fn intern_const_alloc_recursive(
ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
- def_id: DefId,
+ // The `mutability` of the place, ignoring the type.
+ place_mut: Option<hir::Mutability>,
ret: MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let tcx = ecx.tcx;
- // this `mutability` is the mutability of the place, ignoring the type
- let (base_mutability, base_intern_mode) = match tcx.static_mutability(def_id) {
+ let (base_mutability, base_intern_mode) = match place_mut {
Some(hir::Mutability::MutImmutable) => (Mutability::Immutable, InternMode::Static),
// `static mut` doesn't care about interior mutability, it's mutable anyway
Some(hir::Mutability::MutMutable) => (Mutability::Mutable, InternMode::Static),
//! and miri.
use syntax::symbol::Symbol;
+use syntax_pos::Span;
use rustc::ty;
use rustc::ty::layout::{LayoutOf, Primitive, Size};
use rustc::ty::subst::SubstsRef;
Machine, PlaceTy, OpTy, InterpCx,
};
+mod caller_location;
mod type_name;
fn numeric_intrinsic<'tcx, Tag>(
/// Returns `true` if emulation happened.
pub fn emulate_intrinsic(
&mut self,
+ span: Span,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx, M::PointerTag>],
dest: PlaceTy<'tcx, M::PointerTag>,
let intrinsic_name = &self.tcx.item_name(instance.def_id()).as_str()[..];
match intrinsic_name {
+ "caller_location" => {
+ let caller = self.tcx.sess.source_map().lookup_char_pos(span.lo());
+ let location = self.alloc_caller_location(
+ Symbol::intern(&caller.file.name.to_string()),
+ caller.line as u32,
+ caller.col_display as u32 + 1,
+ )?;
+ self.write_scalar(location.ptr, dest)?;
+ }
+
"min_align_of" |
"pref_align_of" |
"needs_drop" |
) -> InterpResult<'tcx, bool> {
let def_id = instance.def_id();
if Some(def_id) == self.tcx.lang_items().panic_fn() {
- assert!(args.len() == 1);
- // &(&'static str, &'static str, u32, u32)
- let place = self.deref_operand(args[0])?;
- let (msg, file, line, col) = (
- self.mplace_field(place, 0)?,
- self.mplace_field(place, 1)?,
- self.mplace_field(place, 2)?,
- self.mplace_field(place, 3)?,
- );
+ // &'static str, &core::panic::Location { &'static str, u32, u32 }
+ assert!(args.len() == 2);
- let msg_place = self.deref_operand(msg.into())?;
+ let msg_place = self.deref_operand(args[0])?;
let msg = Symbol::intern(self.read_str(msg_place)?);
+
+ let location = self.deref_operand(args[1])?;
+ let (file, line, col) = (
+ self.mplace_field(location, 0)?,
+ self.mplace_field(location, 1)?,
+ self.mplace_field(location, 2)?,
+ );
+
let file_place = self.deref_operand(file.into())?;
let file = Symbol::intern(self.read_str(file_place)?);
let line = self.read_scalar(line.into())?.to_u32()?;
--- /dev/null
+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 crate::interpret::{MemoryKind, MPlaceTy, intrinsics::{InterpCx, InterpResult, Machine}};
+
+impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
+ pub fn alloc_caller_location(
+ &mut self,
+ filename: Symbol,
+ line: u32,
+ col: u32,
+ ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+ 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);
+
+ 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 location = self.allocate(loc_layout, MemoryKind::Stack);
+
+ 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)?;
+
+ let layout = &self.tcx.data_layout;
+ let alloc = self.memory.get_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)?;
+
+ Ok(location)
+ }
+}
use rustc::hir::def_id::DefId;
use rustc::mir;
use rustc::ty::{self, Ty, TyCtxt};
+use syntax_pos::Span;
use super::{
Allocation, AllocId, InterpResult, Scalar, AllocationExtra,
/// If this returns successfully, the engine will take care of jumping to the next block.
fn call_intrinsic(
ecx: &mut InterpCx<'mir, 'tcx, Self>,
+ span: Span,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx, Self::PointerTag>],
dest: PlaceTy<'tcx, Self::PointerTag>,
Some(dest) => dest,
None => throw_ub!(Unreachable)
};
- M::call_intrinsic(self, instance, args, dest)?;
+ M::call_intrinsic(self, span, instance, args, dest)?;
// No stack frame gets pushed, the main loop will just act as if the
// call completed.
self.goto_block(ret)?;
providers.const_eval = const_eval::const_eval_provider;
providers.const_eval_raw = const_eval::const_eval_raw_provider;
providers.check_match = hair::pattern::check_match;
+ providers.const_caller_location = const_eval::const_caller_location;
providers.const_field = |tcx, param_env_and_value| {
let (param_env, (value, field)) = param_env_and_value.into_parts();
const_eval::const_field(tcx, param_env, None, field, value)
//! has interior mutability or needs to be dropped, as well as the visitor that emits errors when
//! it finds operations that are invalid in a certain context.
-use rustc::hir::def_id::DefId;
+use rustc::hir::{self, def_id::DefId};
use rustc::mir;
use rustc::ty::{self, TyCtxt};
+use std::fmt;
+
pub use self::qualifs::Qualif;
pub mod ops;
mod resolver;
pub mod validation;
-/// Information about the item currently being validated, as well as a reference to the global
+/// Information about the item currently being const-checked, as well as a reference to the global
/// context.
pub struct Item<'mir, 'tcx> {
- body: &'mir mir::Body<'tcx>,
- tcx: TyCtxt<'tcx>,
- def_id: DefId,
- param_env: ty::ParamEnv<'tcx>,
- mode: validation::Mode,
- for_promotion: bool,
+ pub body: &'mir mir::Body<'tcx>,
+ pub tcx: TyCtxt<'tcx>,
+ pub def_id: DefId,
+ pub param_env: ty::ParamEnv<'tcx>,
+ pub const_kind: Option<ConstKind>,
}
impl Item<'mir, 'tcx> {
body: &'mir mir::Body<'tcx>,
) -> Self {
let param_env = tcx.param_env(def_id);
- let mode = validation::Mode::for_item(tcx, def_id)
- .expect("const validation must only be run inside a const context");
+ let const_kind = ConstKind::for_item(tcx, def_id);
Item {
body,
tcx,
def_id,
param_env,
- mode,
- for_promotion: false,
+ const_kind,
}
}
- // HACK(eddyb) this is to get around the panic for a runtime fn from `Item::new`.
- // Also, it allows promoting `&mut []`.
- pub fn for_promotion(
- tcx: TyCtxt<'tcx>,
- def_id: DefId,
- body: &'mir mir::Body<'tcx>,
- ) -> Self {
- let param_env = tcx.param_env(def_id);
- let mode = validation::Mode::for_item(tcx, def_id)
- .unwrap_or(validation::Mode::ConstFn);
+ /// Returns the kind of const context this `Item` represents (`const`, `static`, etc.).
+ ///
+ /// Panics if this `Item` is not const.
+ pub fn const_kind(&self) -> ConstKind {
+ self.const_kind.expect("`const_kind` must not be called on a non-const fn")
+ }
+}
- Item {
- body,
- tcx,
- def_id,
- param_env,
- mode,
- for_promotion: true,
+/// The kinds of items which require compile-time evaluation.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum ConstKind {
+ /// A `static` item.
+ Static,
+ /// A `static mut` item.
+ StaticMut,
+ /// A `const fn` item.
+ ConstFn,
+ /// A `const` item or an anonymous constant (e.g. in array lengths).
+ Const,
+}
+
+impl ConstKind {
+ /// Returns the validation mode for the item with the given `DefId`, or `None` if this item
+ /// does not require validation (e.g. a non-const `fn`).
+ pub fn for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Self> {
+ use hir::BodyOwnerKind as HirKind;
+
+ let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
+
+ let mode = match tcx.hir().body_owner_kind(hir_id) {
+ HirKind::Closure => return None,
+
+ HirKind::Fn if tcx.is_const_fn(def_id) => ConstKind::ConstFn,
+ HirKind::Fn => return None,
+
+ HirKind::Const => ConstKind::Const,
+
+ HirKind::Static(hir::MutImmutable) => ConstKind::Static,
+ HirKind::Static(hir::MutMutable) => ConstKind::StaticMut,
+ };
+
+ Some(mode)
+ }
+
+ pub fn is_static(self) -> bool {
+ match self {
+ ConstKind::Static | ConstKind::StaticMut => true,
+ ConstKind::ConstFn | ConstKind::Const => false,
+ }
+ }
+
+ /// Returns `true` if the value returned by this item must be `Sync`.
+ ///
+ /// This returns false for `StaticMut` since all accesses to one are `unsafe` anyway.
+ pub fn requires_sync(self) -> bool {
+ match self {
+ ConstKind::Static => true,
+ ConstKind::ConstFn | ConstKind::Const | ConstKind::StaticMut => false,
}
}
}
+impl fmt::Display for ConstKind {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ ConstKind::Const => write!(f, "constant"),
+ ConstKind::Static | ConstKind::StaticMut => write!(f, "static"),
+ ConstKind::ConstFn => write!(f, "constant function"),
+ }
+ }
+}
-fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
+/// Returns `true` if this `DefId` points to one of the official `panic` lang items.
+pub fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
Some(def_id) == tcx.lang_items().panic_fn() ||
Some(def_id) == tcx.lang_items().begin_panic_fn()
}
use syntax::symbol::sym;
use syntax_pos::{Span, Symbol};
-use super::Item;
-use super::validation::Mode;
+use super::{ConstKind, Item};
/// An operation that is not *always* allowed in a const context.
pub trait NonConstOp: std::fmt::Debug {
span,
E0019,
"{} contains unimplemented expression type",
- item.mode
+ item.const_kind()
);
if item.tcx.sess.teach(&err.get_code().unwrap()) {
err.note("A function call isn't allowed in the const's initialization expression \
E0015,
"calls in {}s are limited to constant functions, \
tuple structs and tuple variants",
- item.mode,
+ item.const_kind(),
);
err.emit();
}
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
let mut err = struct_span_err!(item.tcx.sess, span, E0010,
- "allocations are not allowed in {}s", item.mode);
- err.span_label(span, format!("allocation not allowed in {}s", item.mode));
+ "allocations are not allowed in {}s", item.const_kind());
+ err.span_label(span, format!("allocation not allowed in {}s", item.const_kind()));
if item.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"The value of statics and constants must be known at compile time, \
struct_span_err!(item.tcx.sess, span, E0493,
"destructors cannot be evaluated at compile-time")
.span_label(span, format!("{}s cannot evaluate destructors",
- item.mode))
+ item.const_kind()))
.emit();
}
}
if let BorrowKind::Mut { .. } = kind {
let mut err = struct_span_err!(item.tcx.sess, span, E0017,
"references in {}s may only refer \
- to immutable values", item.mode);
+ to immutable values", item.const_kind());
err.span_label(span, format!("{}s require immutable values",
- item.mode));
+ item.const_kind()));
if item.tcx.sess.teach(&err.get_code().unwrap()) {
err.note("References in statics and constants may only refer \
to immutable values.\n\n\
sym::const_panic,
span,
GateIssue::Language,
- &format!("panicking in {}s is unstable", item.mode),
+ &format!("panicking in {}s is unstable", item.const_kind()),
);
}
}
sym::const_compare_raw_pointers,
span,
GateIssue::Language,
- &format!("comparing raw pointers inside {}", item.mode),
+ &format!("comparing raw pointers inside {}", item.const_kind()),
);
}
}
span, GateIssue::Language,
&format!(
"dereferencing raw pointers in {}s is unstable",
- item.mode,
+ item.const_kind(),
),
);
}
span, GateIssue::Language,
&format!(
"casting pointers to integers in {}s is unstable",
- item.mode,
+ item.const_kind(),
),
);
}
pub struct StaticAccess;
impl NonConstOp for StaticAccess {
fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
- item.mode.is_static()
+ item.const_kind().is_static()
}
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
let mut err = struct_span_err!(item.tcx.sess, span, E0013,
"{}s cannot refer to statics, use \
- a constant instead", item.mode);
+ a constant instead", item.const_kind());
if item.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"Static and const variables can refer to other const variables. \
&item.tcx.sess.parse_sess, sym::const_transmute,
span, GateIssue::Language,
&format!("The use of std::mem::transmute() \
- is gated in {}s", item.mode));
+ is gated in {}s", item.const_kind()));
}
}
impl NonConstOp for UnionAccess {
fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
// Union accesses are stable in all contexts except `const fn`.
- item.mode != Mode::ConstFn || Self::feature_gate(item.tcx).unwrap()
+ item.const_kind() != ConstKind::ConstFn || Self::feature_gate(item.tcx).unwrap()
}
fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
use rustc::ty::{self, Ty};
use syntax_pos::DUMMY_SP;
-use super::Item as ConstCx;
-use super::validation::Mode;
+use super::{ConstKind, Item as ConstCx};
#[derive(Clone, Copy)]
pub struct QualifSet(u8);
// mutably without consequences.
match ty.kind {
// Inside a `static mut`, &mut [...] is also allowed.
- ty::Array(..) | ty::Slice(_) if cx.mode == Mode::StaticMut => {},
-
- // FIXME(eddyb) the `cx.for_promotion` condition
- // 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.for_promotion => {},
+ | 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,
}
//! Propagate `Qualif`s between locals and query the results.
//!
-//! This also contains the dataflow analysis used to track `Qualif`s on complex control-flow
-//! graphs.
+//! This contains the dataflow analysis used to track `Qualif`s on complex control-flow graphs.
use rustc::mir::visit::Visitor;
use rustc::mir::{self, BasicBlock, Local, Location};
use rustc_index::bit_set::BitSet;
-use std::cell::RefCell;
use std::marker::PhantomData;
use crate::dataflow::{self as old_dataflow, generic as dataflow};
use super::{Item, Qualif};
-use self::old_dataflow::IndirectlyMutableLocals;
/// A `Visitor` that propagates qualifs between locals. This defines the transfer function of
-/// `FlowSensitiveAnalysis` as well as the logic underlying `TempPromotionResolver`.
+/// `FlowSensitiveAnalysis`.
///
/// This transfer does nothing when encountering an indirect assignment. Consumers should rely on
/// the `IndirectlyMutableLocals` dataflow pass to see if a `Local` may have become qualified via
}
}
-/// Types that can compute the qualifs of each local at each location in a `mir::Body`.
-///
-/// Code that wishes to use a `QualifResolver` must call `visit_{statement,terminator}` for each
-/// statement or terminator, processing blocks in reverse post-order beginning from the
-/// `START_BLOCK`. Calling code may optionally call `get` after visiting each statement or
-/// terminator to query the qualification state immediately before that statement or terminator.
-///
-/// These conditions are much more restrictive than woud be required by `FlowSensitiveResolver`
-/// alone. This is to allow a linear, on-demand `TempPromotionResolver` that can operate
-/// efficiently on simple CFGs.
-pub trait QualifResolver<Q> {
- /// Get the qualifs of each local at the last location visited.
- ///
- /// This takes `&mut self` so qualifs can be computed lazily.
- fn get(&mut self) -> &BitSet<Local>;
-
- /// A convenience method for `self.get().contains(local)`.
- fn contains(&mut self, local: Local) -> bool {
- self.get().contains(local)
- }
-
- /// Resets the resolver to the `START_BLOCK`. This allows a resolver to be reused
- /// for multiple passes over a `mir::Body`.
- fn reset(&mut self);
-}
-
-pub type IndirectlyMutableResults<'mir, 'tcx> =
- old_dataflow::DataflowResultsCursor<'mir, 'tcx, IndirectlyMutableLocals<'mir, 'tcx>>;
-
-/// A resolver for qualifs that works on arbitrarily complex CFGs.
-///
-/// As soon as a `Local` becomes writable through a reference (as determined by the
-/// `IndirectlyMutableLocals` dataflow pass), we must assume that it takes on all other qualifs
-/// possible for its type. This is because no effort is made to track qualifs across indirect
-/// assignments (e.g. `*p = x` or calls to opaque functions).
-///
-/// It is possible to be more precise here by waiting until an indirect assignment actually occurs
-/// before marking a borrowed `Local` as qualified.
-pub struct FlowSensitiveResolver<'a, 'mir, 'tcx, Q>
-where
- Q: Qualif,
-{
- location: Location,
- indirectly_mutable_locals: &'a RefCell<IndirectlyMutableResults<'mir, 'tcx>>,
- cursor: dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q>>,
- qualifs_per_local: BitSet<Local>,
-
- /// The value of `Q::in_any_value_of_ty` for each local.
- qualifs_in_any_value_of_ty: BitSet<Local>,
-}
-
-impl<Q> FlowSensitiveResolver<'a, 'mir, 'tcx, Q>
-where
- Q: Qualif,
-{
- pub fn new(
- _: Q,
- item: &'a Item<'mir, 'tcx>,
- indirectly_mutable_locals: &'a RefCell<IndirectlyMutableResults<'mir, 'tcx>>,
- dead_unwinds: &BitSet<BasicBlock>,
- ) -> Self {
- let analysis = FlowSensitiveAnalysis {
- item,
- _qualif: PhantomData,
- };
- let results =
- 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 mut qualifs_in_any_value_of_ty = BitSet::new_empty(item.body.local_decls.len());
- for (local, decl) in item.body.local_decls.iter_enumerated() {
- if Q::in_any_value_of_ty(item, decl.ty) {
- qualifs_in_any_value_of_ty.insert(local);
- }
- }
-
- FlowSensitiveResolver {
- cursor,
- indirectly_mutable_locals,
- qualifs_per_local: BitSet::new_empty(item.body.local_decls.len()),
- qualifs_in_any_value_of_ty,
- location: Location { block: mir::START_BLOCK, statement_index: 0 },
- }
- }
-}
-
-impl<Q> Visitor<'tcx> for FlowSensitiveResolver<'_, '_, 'tcx, Q>
-where
- Q: Qualif
-{
- fn visit_statement(&mut self, _: &mir::Statement<'tcx>, location: Location) {
- self.location = location;
- }
-
- fn visit_terminator(&mut self, _: &mir::Terminator<'tcx>, location: Location) {
- self.location = location;
- }
-}
-
-impl<Q> QualifResolver<Q> for FlowSensitiveResolver<'_, '_, '_, Q>
-where
- Q: Qualif
-{
- fn get(&mut self) -> &BitSet<Local> {
- let mut indirectly_mutable_locals = self.indirectly_mutable_locals.borrow_mut();
-
- indirectly_mutable_locals.seek(self.location);
- self.cursor.seek_before(self.location);
-
- self.qualifs_per_local.overwrite(indirectly_mutable_locals.get());
- self.qualifs_per_local.union(self.cursor.get());
- self.qualifs_per_local.intersect(&self.qualifs_in_any_value_of_ty);
- &self.qualifs_per_local
- }
-
- fn contains(&mut self, local: Local) -> bool {
- // No need to update the cursor if we know that `Local` cannot possibly be qualified.
- if !self.qualifs_in_any_value_of_ty.contains(local) {
- return false;
- }
-
- // Otherwise, return `true` if this local is qualified or was indirectly mutable at any
- // point before this statement.
- self.cursor.seek_before(self.location);
- if self.cursor.get().contains(local) {
- return true;
- }
-
- let mut indirectly_mutable_locals = self.indirectly_mutable_locals.borrow_mut();
- indirectly_mutable_locals.seek(self.location);
- indirectly_mutable_locals.get().contains(local)
- }
-
- fn reset(&mut self) {
- self.location = Location { block: mir::START_BLOCK, statement_index: 0 };
- }
-}
-
/// The dataflow analysis used to propagate qualifs on arbitrary CFGs.
pub(super) struct FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q> {
item: &'a Item<'mir, 'tcx>,
where
Q: Qualif,
{
+ pub(super) fn new(_: Q, item: &'a Item<'mir, 'tcx>) -> Self {
+ FlowSensitiveAnalysis {
+ item,
+ _qualif: PhantomData,
+ }
+ }
+
fn transfer_function(
&self,
state: &'a mut BitSet<Local>,
//! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.
-use rustc::hir::{self, def_id::DefId};
use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
use rustc::mir::*;
use rustc::ty::cast::CastTy;
-use rustc::ty::{self, TyCtxt};
+use rustc::ty;
use rustc_index::bit_set::BitSet;
use rustc_target::spec::abi::Abi;
use syntax::symbol::sym;
use syntax_pos::Span;
-use std::cell::RefCell;
use std::fmt;
use std::ops::Deref;
-use crate::dataflow as old_dataflow;
-use super::{Item, Qualif, is_lang_panic_fn};
-use super::resolver::{FlowSensitiveResolver, IndirectlyMutableResults, QualifResolver};
-use super::qualifs::{HasMutInterior, NeedsDrop};
+use crate::dataflow::{self as old_dataflow, generic as dataflow};
+use self::old_dataflow::IndirectlyMutableLocals;
use super::ops::{self, NonConstOp};
+use super::qualifs::{HasMutInterior, NeedsDrop};
+use super::resolver::FlowSensitiveAnalysis;
+use super::{ConstKind, Item, Qualif, is_lang_panic_fn};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum CheckOpResult {
Allowed,
}
-/// What kind of item we are in.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum Mode {
- /// A `static` item.
- Static,
- /// A `static mut` item.
- StaticMut,
- /// A `const fn` item.
- ConstFn,
- /// A `const` item or an anonymous constant (e.g. in array lengths).
- Const,
-}
-
-impl Mode {
- /// Returns the validation mode for the item with the given `DefId`, or `None` if this item
- /// does not require validation (e.g. a non-const `fn`).
- pub fn for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Self> {
- use hir::BodyOwnerKind as HirKind;
-
- let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
+pub type IndirectlyMutableResults<'mir, 'tcx> =
+ old_dataflow::DataflowResultsCursor<'mir, 'tcx, IndirectlyMutableLocals<'mir, 'tcx>>;
- let mode = match tcx.hir().body_owner_kind(hir_id) {
- HirKind::Closure => return None,
-
- HirKind::Fn if tcx.is_const_fn(def_id) => Mode::ConstFn,
- HirKind::Fn => return None,
-
- HirKind::Const => Mode::Const,
+struct QualifCursor<'a, 'mir, 'tcx, Q: Qualif> {
+ cursor: dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q>>,
+ in_any_value_of_ty: BitSet<Local>,
+}
- HirKind::Static(hir::MutImmutable) => Mode::Static,
- HirKind::Static(hir::MutMutable) => Mode::StaticMut,
- };
+impl<Q: Qualif> QualifCursor<'a, 'mir, 'tcx, Q> {
+ pub fn new(
+ q: Q,
+ item: &'a Item<'mir, 'tcx>,
+ dead_unwinds: &BitSet<BasicBlock>,
+ ) -> Self {
+ let analysis = FlowSensitiveAnalysis::new(q, item);
+ let results =
+ 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 mut in_any_value_of_ty = BitSet::new_empty(item.body.local_decls.len());
+ for (local, decl) in item.body.local_decls.iter_enumerated() {
+ if Q::in_any_value_of_ty(item, decl.ty) {
+ in_any_value_of_ty.insert(local);
+ }
+ }
- Some(mode)
+ QualifCursor {
+ cursor,
+ in_any_value_of_ty,
+ }
}
+}
- pub fn is_static(self) -> bool {
- match self {
- Mode::Static | Mode::StaticMut => true,
- Mode::ConstFn | Mode::Const => false,
- }
+pub struct Qualifs<'a, 'mir, 'tcx> {
+ has_mut_interior: QualifCursor<'a, 'mir, 'tcx, HasMutInterior>,
+ needs_drop: QualifCursor<'a, 'mir, 'tcx, NeedsDrop>,
+ indirectly_mutable: IndirectlyMutableResults<'mir, 'tcx>,
+}
+
+impl Qualifs<'a, 'mir, 'tcx> {
+ fn indirectly_mutable(&mut self, local: Local, location: Location) -> bool {
+ self.indirectly_mutable.seek(location);
+ self.indirectly_mutable.get().contains(local)
}
- /// Returns `true` if the value returned by this item must be `Sync`.
+ /// Returns `true` if `local` is `NeedsDrop` at the given `Location`.
///
- /// This returns false for `StaticMut` since all accesses to one are `unsafe` anyway.
- pub fn requires_sync(self) -> bool {
- match self {
- Mode::Static => true,
- Mode::ConstFn | Mode::Const | Mode::StaticMut => false,
+ /// Only updates the cursor if absolutely necessary
+ fn needs_drop_lazy_seek(&mut self, local: Local, location: Location) -> bool {
+ if !self.needs_drop.in_any_value_of_ty.contains(local) {
+ return false;
}
+
+ self.needs_drop.cursor.seek_before(location);
+ self.needs_drop.cursor.get().contains(local)
+ || self.indirectly_mutable(local, location)
}
-}
-impl fmt::Display for Mode {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match *self {
- Mode::Const => write!(f, "constant"),
- Mode::Static | Mode::StaticMut => write!(f, "static"),
- Mode::ConstFn => write!(f, "constant function"),
+ /// Returns `true` if `local` is `HasMutInterior`, but requires the `has_mut_interior` and
+ /// `indirectly_mutable` cursors to be updated beforehand.
+ fn has_mut_interior_eager_seek(&self, local: Local) -> bool {
+ if !self.has_mut_interior.in_any_value_of_ty.contains(local) {
+ return false;
}
- }
-}
-pub struct Qualifs<'a, 'mir, 'tcx> {
- has_mut_interior: FlowSensitiveResolver<'a, 'mir, 'tcx, HasMutInterior>,
- needs_drop: FlowSensitiveResolver<'a, 'mir, 'tcx, NeedsDrop>,
+ self.has_mut_interior.cursor.get().contains(local)
+ || self.indirectly_mutable.get().contains(local)
+ }
}
pub struct Validator<'a, 'mir, 'tcx> {
}
}
-pub fn compute_indirectly_mutable_locals<'mir, 'tcx>(
- item: &Item<'mir, 'tcx>,
-) -> RefCell<IndirectlyMutableResults<'mir, 'tcx>> {
- let dead_unwinds = BitSet::new_empty(item.body.basic_blocks().len());
-
- let indirectly_mutable_locals = old_dataflow::do_dataflow(
- item.tcx,
- 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),
- |_, local| old_dataflow::DebugFormatted::new(&local),
- );
-
- let indirectly_mutable_locals = old_dataflow::DataflowResultsCursor::new(
- indirectly_mutable_locals,
- item.body,
- );
-
- RefCell::new(indirectly_mutable_locals)
-}
-
impl Validator<'a, 'mir, 'tcx> {
pub fn new(
item: &'a Item<'mir, 'tcx>,
- indirectly_mutable_locals: &'a RefCell<IndirectlyMutableResults<'mir, 'tcx>>,
) -> Self {
let dead_unwinds = BitSet::new_empty(item.body.basic_blocks().len());
- let needs_drop = FlowSensitiveResolver::new(
+ let needs_drop = QualifCursor::new(
NeedsDrop,
item,
- indirectly_mutable_locals,
&dead_unwinds,
);
- let has_mut_interior = FlowSensitiveResolver::new(
+ let has_mut_interior = QualifCursor::new(
HasMutInterior,
item,
- indirectly_mutable_locals,
&dead_unwinds,
);
+ let indirectly_mutable = old_dataflow::do_dataflow(
+ item.tcx,
+ 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),
+ |_, local| old_dataflow::DebugFormatted::new(&local),
+ );
+
+ let indirectly_mutable = old_dataflow::DataflowResultsCursor::new(
+ indirectly_mutable,
+ item.body,
+ );
+
let qualifs = Qualifs {
needs_drop,
has_mut_interior,
+ indirectly_mutable,
};
Validator {
}
}
- /// Resets the `QualifResolver`s used by this `Validator` and returns them so they can be
- /// reused.
- pub fn into_qualifs(mut self) -> Qualifs<'a, 'mir, 'tcx> {
- self.qualifs.needs_drop.reset();
- self.qualifs.has_mut_interior.reset();
- self.qualifs
- }
-
pub fn take_errors(&mut self) -> Vec<(Span, String)> {
std::mem::replace(&mut self.errors, vec![])
}
let is_thread_local = self.tcx.has_attr(*def_id, sym::thread_local);
if is_thread_local {
self.check_op(ops::ThreadLocalAccess);
- } else if self.mode == Mode::Static && context.is_mutating_use() {
+ } else if self.const_kind() == ConstKind::Static && context.is_mutating_use() {
// this is not strictly necessary as miri will also bail out
// For interior mutability we can't really catch this statically as that
// goes through raw pointers and intermediate temporaries, so miri has
// 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 {
- let rvalue_has_mut_interior = {
- let has_mut_interior = self.qualifs.has_mut_interior.get();
- HasMutInterior::in_rvalue(&self.item, &|l| has_mut_interior.contains(l), 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() {
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
trace!("visit_statement: statement={:?} location={:?}", statement, location);
- self.qualifs.needs_drop.visit_statement(statement, location);
- self.qualifs.has_mut_interior.visit_statement(statement, location);
-
match statement.kind {
StatementKind::Assign(..) => {
self.super_statement(statement, location);
}
}
- fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
- trace!("visit_terminator: terminator={:?} location={:?}", terminator, location);
-
- self.qualifs.needs_drop.visit_terminator(terminator, location);
- self.qualifs.has_mut_interior.visit_terminator(terminator, location);
-
- self.super_terminator(terminator, location);
- }
-
fn visit_terminator_kind(&mut self, kind: &TerminatorKind<'tcx>, location: Location) {
trace!("visit_terminator_kind: kind={:?} location={:?}", kind, location);
self.super_terminator_kind(kind, location);
let needs_drop = if let Some(local) = dropped_place.as_local() {
// Use the span where the local was declared as the span of the drop error.
err_span = self.body.local_decls[local].source_info.span;
- self.qualifs.needs_drop.contains(local)
+ self.qualifs.needs_drop_lazy_seek(local, location)
} else {
true
};
fn call_intrinsic(
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
+ _span: Span,
_instance: ty::Instance<'tcx>,
_args: &[OpTy<'tcx>],
_dest: PlaceTy<'tcx>,
//! initialization and can otherwise silence errors, if
//! move analysis runs after promotion on broken MIR.
-use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::mir::*;
use rustc::mir::interpret::ConstValue;
use std::{iter, mem, usize};
-use crate::transform::check_consts::{qualifs, Item as ConstCx};
+use crate::transform::check_consts::{qualifs, Item, ConstKind, is_lang_panic_fn};
/// State of a temporary during collection and promotion.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
(collector.temps, collector.candidates)
}
+/// Checks whether locals that appear in a promotion context (`Candidate`) are actually promotable.
+///
+/// This wraps an `Item`, and has access to all fields of that `Item` via `Deref` coercion.
struct Validator<'a, 'tcx> {
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- body: &'a Body<'tcx>,
- is_static: bool,
- is_static_mut: bool,
- is_non_const_fn: bool,
+ item: Item<'a, 'tcx>,
temps: &'a IndexVec<Local, TempState>,
- // FIXME(eddyb) deduplicate the data in this vs other fields.
- const_cx: ConstCx<'a, 'tcx>,
-
/// Explicit promotion happens e.g. for constant arguments declared via
/// `rustc_args_required_const`.
/// Implicit promotion has almost the same rules, except that disallows `const fn`
explicit: bool,
}
-struct Unpromotable;
+impl std::ops::Deref for Validator<'a, 'tcx> {
+ type Target = Item<'a, 'tcx>;
-impl<'tcx> Validator<'_, 'tcx> {
- fn is_const_panic_fn(&self, def_id: DefId) -> bool {
- Some(def_id) == self.tcx.lang_items().panic_fn() ||
- Some(def_id) == self.tcx.lang_items().begin_panic_fn()
+ fn deref(&self) -> &Self::Target {
+ &self.item
}
+}
+
+struct Unpromotable;
+impl<'tcx> Validator<'_, 'tcx> {
fn validate_candidate(&self, candidate: Candidate) -> Result<(), Unpromotable> {
match candidate {
Candidate::Ref(loc) => {
if self.qualif_local::<qualifs::NeedsDrop>(base) {
return Err(Unpromotable);
}
+
if let BorrowKind::Mut { .. } = kind {
let ty = place.ty(self.body, self.tcx).ty;
// In theory, any zero-sized value could be borrowed
// mutably without consequences. However, only &mut []
// is allowed right now, and only in functions.
- if self.is_static_mut {
+ if self.const_kind == Some(ConstKind::StaticMut) {
// Inside a `static mut`, &mut [...] is also allowed.
match ty.kind {
ty::Array(..) | ty::Slice(_) => {}
// FIXME(eddyb) the `self.is_non_const_fn` condition
// seems unnecessary, given that this is merely a ZST.
match len.try_eval_usize(self.tcx, self.param_env) {
- Some(0) if self.is_non_const_fn => {},
+ Some(0) if self.const_kind.is_none() => {},
_ => return Err(Unpromotable),
}
} else {
let statement = &self.body[loc.block].statements[loc.statement_index];
match &statement.kind {
StatementKind::Assign(box(_, rhs)) => {
- Q::in_rvalue(&self.const_cx, per_local, rhs)
+ Q::in_rvalue(&self.item, per_local, rhs)
}
_ => {
span_bug!(statement.source_info.span, "{:?} is not an assignment",
match &terminator.kind {
TerminatorKind::Call { func, args, .. } => {
let return_ty = self.body.local_decls[local].ty;
- Q::in_call(&self.const_cx, per_local, func, args, return_ty)
+ Q::in_call(&self.item, per_local, func, args, return_ty)
}
kind => {
span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
} => {
// Only allow statics (not consts) to refer to other statics.
// FIXME(eddyb) does this matter at all for promotion?
- let allowed = self.is_static || self.is_static_mut;
- if !allowed {
+ let is_static = self.const_kind.map_or(false, |k| k.is_static());
+ if !is_static {
return Err(Unpromotable);
}
}
ProjectionElem::Field(..) => {
- if self.is_non_const_fn {
+ if self.const_kind.is_none() {
let base_ty =
Place::ty_from(place.base, proj_base, self.body, self.tcx).ty;
if let Some(def) = base_ty.ty_adt_def() {
fn validate_rvalue(&self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> {
match *rvalue {
- Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if self.is_non_const_fn => {
+ Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if self.const_kind.is_none() => {
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 self.is_non_const_fn => {
+ Rvalue::BinaryOp(op, ref lhs, _) if self.const_kind.is_none() => {
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 ||
// In theory, any zero-sized value could be borrowed
// mutably without consequences. However, only &mut []
// is allowed right now, and only in functions.
- if self.is_static_mut {
+ if self.const_kind == Some(ConstKind::StaticMut) {
// Inside a `static mut`, &mut [...] is also allowed.
match ty.kind {
ty::Array(..) | ty::Slice(_) => {}
_ => return Err(Unpromotable),
}
} else if let ty::Array(_, len) = ty.kind {
- // FIXME(eddyb) the `self.is_non_const_fn` condition
- // seems unnecessary, given that this is merely a ZST.
+ // FIXME(eddyb): We only return `Unpromotable` for `&mut []` inside a
+ // const context which seems unnecessary given that this is merely a ZST.
match len.try_eval_usize(self.tcx, self.param_env) {
- Some(0) if self.is_non_const_fn => {},
+ Some(0) if self.const_kind.is_none() => {},
_ => return Err(Unpromotable),
}
} else {
) -> Result<(), Unpromotable> {
let fn_ty = callee.ty(self.body, self.tcx);
- if !self.explicit && self.is_non_const_fn {
+ if !self.explicit && self.const_kind.is_none() {
if let ty::FnDef(def_id, _) = fn_ty.kind {
// Never promote runtime `const fn` calls of
// functions without `#[rustc_promotable]`.
ty::FnDef(def_id, _) => {
self.tcx.is_const_fn(def_id) ||
self.tcx.is_unstable_const_fn(def_id).is_some() ||
- self.is_const_panic_fn(def_id)
+ is_lang_panic_fn(self.tcx, self.def_id)
}
_ => false,
};
}
}
+// FIXME(eddyb) remove the differences for promotability in `static`, `const`, `const fn`.
pub fn validate_candidates(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
candidates: &[Candidate],
) -> Vec<Candidate> {
let mut validator = Validator {
- tcx,
- param_env: tcx.param_env(def_id),
- body,
- is_static: false,
- is_static_mut: false,
- is_non_const_fn: false,
+ item: Item::new(tcx, def_id, body),
temps,
-
- const_cx: ConstCx::for_promotion(tcx, def_id, body),
-
explicit: false,
};
- // FIXME(eddyb) remove the distinctions that make this necessary.
- let id = tcx.hir().as_local_hir_id(def_id).unwrap();
- match tcx.hir().body_owner_kind(id) {
- hir::BodyOwnerKind::Closure => validator.is_non_const_fn = true,
- hir::BodyOwnerKind::Fn => {
- if !tcx.is_const_fn(def_id) {
- validator.is_non_const_fn = true;
- }
- },
- hir::BodyOwnerKind::Static(hir::MutImmutable) => validator.is_static = true,
- hir::BodyOwnerKind::Static(hir::MutMutable) => validator.is_static_mut = true,
- _ => {}
- }
-
candidates.iter().copied().filter(|&candidate| {
validator.explicit = match candidate {
Candidate::Ref(_) |
}
let item = new_checker::Item::new(self.tcx, self.def_id, self.body);
- let mut_borrowed_locals = new_checker::validation::compute_indirectly_mutable_locals(&item);
- let mut validator = new_checker::validation::Validator::new(&item, &mut_borrowed_locals);
+ let mut validator = new_checker::validation::Validator::new(&item);
validator.suppress_errors = !use_new_validator;
self.suppress_errors = use_new_validator;
```
"##,
+E0666: r##"
+`impl Trait` types cannot appear nested in the
+generic arguments of other `impl Trait` types.
+
+Example of erroneous code:
+
+```compile_fail,E0666
+trait MyGenericTrait<T> {}
+trait MyInnerTrait {}
+
+fn foo(bar: impl MyGenericTrait<impl MyInnerTrait>) {}
+```
+
+Type parameters for `impl Trait` types must be
+explicitly defined as named generic parameters:
+
+```
+trait MyGenericTrait<T> {}
+trait MyInnerTrait {}
+
+fn foo<T: MyInnerTrait>(bar: impl MyGenericTrait<T>) {}
+```
+"##,
+
E0695: r##"
A `break` statement without a label appeared inside a labeled block.
;
E0226, // only a single explicit lifetime bound is permitted
E0472, // asm! is unsupported on this target
- E0666, // nested `impl Trait` is illegal
E0667, // `impl Trait` in projections
E0696, // `continue` pointing to a labeled block
E0706, // `async fn` in trait
}
}
-struct LateResolutionVisitor<'a, 'b> {
- r: &'b mut Resolver<'a>,
-
- /// The module that represents the current item scope.
- parent_scope: ParentScope<'a>,
-
- /// The current set of local scopes for types and values.
- /// FIXME #4948: Reuse ribs to avoid allocation.
- ribs: PerNS<Vec<Rib<'a>>>,
-
- /// The current set of local scopes, for labels.
- label_ribs: Vec<Rib<'a, NodeId>>,
-
- /// The trait that the current context can refer to.
- current_trait_ref: Option<(Module<'a>, TraitRef)>,
-
+#[derive(Default)]
+struct DiagnosticMetadata {
/// The current trait's associated types' ident, used for diagnostic suggestions.
current_trait_assoc_types: Vec<Ident>,
/// Only used for better errors on `fn(): fn()`.
current_type_ascription: Vec<Span>,
+
+ /// Only used for better errors on `let <pat>: <expr, not type>;`.
+ current_let_binding: Option<(Span, Option<Span>, Option<Span>)>,
+}
+
+struct LateResolutionVisitor<'a, 'b> {
+ r: &'b mut Resolver<'a>,
+
+ /// The module that represents the current item scope.
+ parent_scope: ParentScope<'a>,
+
+ /// The current set of local scopes for types and values.
+ /// FIXME #4948: Reuse ribs to avoid allocation.
+ ribs: PerNS<Vec<Rib<'a>>>,
+
+ /// The current set of local scopes, for labels.
+ label_ribs: Vec<Rib<'a, NodeId>>,
+
+ /// The trait that the current context can refer to.
+ current_trait_ref: Option<(Module<'a>, TraitRef)>,
+
+ /// Fields used to add information to diagnostic errors.
+ diagnostic_metadata: DiagnosticMetadata,
}
/// Walks the whole crate in DFS order, visiting each item, resolving names as it goes.
self.resolve_expr(expr, None);
}
fn visit_local(&mut self, local: &'tcx Local) {
+ let local_spans = match local.pat.kind {
+ // We check for this to avoid tuple struct fields.
+ PatKind::Wild => None,
+ _ => Some((
+ local.pat.span,
+ local.ty.as_ref().map(|ty| ty.span),
+ local.init.as_ref().map(|init| init.span),
+ )),
+ };
+ let original = replace(&mut self.diagnostic_metadata.current_let_binding, local_spans);
self.resolve_local(local);
+ self.diagnostic_metadata.current_let_binding = original;
}
fn visit_ty(&mut self, ty: &'tcx Ty) {
match ty.kind {
}
}
fn visit_fn(&mut self, fn_kind: FnKind<'tcx>, declaration: &'tcx FnDecl, sp: Span, _: NodeId) {
- let previous_value = replace(&mut self.current_function, Some(sp));
+ let previous_value = replace(&mut self.diagnostic_metadata.current_function, Some(sp));
debug!("(resolving function) entering function");
let rib_kind = match fn_kind {
FnKind::ItemFn(..) => FnItemRibKind,
debug!("(resolving function) leaving function");
})
});
- self.current_function = previous_value;
+ self.diagnostic_metadata.current_function = previous_value;
}
fn visit_generics(&mut self, generics: &'tcx Generics) {
// (We however cannot ban `Self` for defaults on *all* generic
// lists; e.g. trait generics can usefully refer to `Self`,
// such as in the case of `trait Add<Rhs = Self>`.)
- if self.current_self_item.is_some() { // (`Some` if + only if we are in ADT's generics.)
+ if self.diagnostic_metadata.current_self_item.is_some() {
+ // (`Some` if + only if we are in ADT's generics.)
default_ban_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), Res::Err);
}
},
label_ribs: Vec::new(),
current_trait_ref: None,
- current_trait_assoc_types: Vec::new(),
- current_self_type: None,
- current_self_item: None,
- current_function: None,
- unused_labels: Default::default(),
- current_type_ascription: Vec::new(),
+ diagnostic_metadata: DiagnosticMetadata::default(),
}
}
fn with_current_self_type<T>(&mut self, self_type: &Ty, f: impl FnOnce(&mut Self) -> T) -> T {
// Handle nested impls (inside fn bodies)
- let previous_value = replace(&mut self.current_self_type, Some(self_type.clone()));
+ let previous_value = replace(
+ &mut self.diagnostic_metadata.current_self_type,
+ Some(self_type.clone()),
+ );
let result = f(self);
- self.current_self_type = previous_value;
+ self.diagnostic_metadata.current_self_type = previous_value;
result
}
fn with_current_self_item<T>(&mut self, self_item: &Item, f: impl FnOnce(&mut Self) -> T) -> T {
- let previous_value = replace(&mut self.current_self_item, Some(self_item.id));
+ let previous_value = replace(
+ &mut self.diagnostic_metadata.current_self_item,
+ Some(self_item.id),
+ );
let result = f(self);
- self.current_self_item = previous_value;
+ self.diagnostic_metadata.current_self_item = previous_value;
result
}
f: impl FnOnce(&mut Self) -> T,
) -> T {
let trait_assoc_types = replace(
- &mut self.current_trait_assoc_types,
+ &mut self.diagnostic_metadata.current_trait_assoc_types,
trait_items.iter().filter_map(|item| match &item.kind {
TraitItemKind::Type(bounds, _) if bounds.len() == 0 => Some(item.ident),
_ => None,
}).collect(),
);
let result = f(self);
- self.current_trait_assoc_types = trait_assoc_types;
+ self.diagnostic_metadata.current_trait_assoc_types = trait_assoc_types;
result
}
fn with_resolved_label(&mut self, label: Option<Label>, id: NodeId, f: impl FnOnce(&mut Self)) {
if let Some(label) = label {
- self.unused_labels.insert(id, label.ident.span);
+ self.diagnostic_metadata.unused_labels.insert(id, label.ident.span);
self.with_label_rib(NormalRibKind, |this| {
let ident = label.ident.modern_and_legacy();
this.label_ribs.last_mut().unwrap().bindings.insert(ident, id);
Some(node_id) => {
// Since this res is a label, it is never read.
self.r.label_res_map.insert(expr.id, node_id);
- self.unused_labels.remove(&node_id);
+ self.diagnostic_metadata.unused_labels.remove(&node_id);
}
}
}
}
ExprKind::Type(ref type_expr, _) => {
- self.current_type_ascription.push(type_expr.span);
+ self.diagnostic_metadata.current_type_ascription.push(type_expr.span);
visit::walk_expr(self, expr);
- self.current_type_ascription.pop();
+ self.diagnostic_metadata.current_type_ascription.pop();
}
// `async |x| ...` gets desugared to `|x| future_from_generator(|| ...)`, so we need to
// resolve the arguments within the proper scopes so that usages of them inside the
pub(crate) fn late_resolve_crate(&mut self, krate: &Crate) {
let mut late_resolution_visitor = LateResolutionVisitor::new(self);
visit::walk_crate(&mut late_resolution_visitor, krate);
- for (id, span) in late_resolution_visitor.unused_labels.iter() {
+ for (id, span) in late_resolution_visitor.diagnostic_metadata.unused_labels.iter() {
self.session.buffer_lint(lint::builtin::UNUSED_LABELS, *id, *span, "unused label");
}
}
let path_str = Segment::names_to_string(path);
let item_str = path.last().unwrap().ident;
let code = source.error_code(res.is_some());
- let (base_msg, fallback_label, base_span) = if let Some(res) = res {
+ let (base_msg, fallback_label, base_span, could_be_expr) = if let Some(res) = res {
(format!("expected {}, found {} `{}`", expected, res.descr(), path_str),
format!("not a {}", expected),
- span)
+ span,
+ match res {
+ Res::Def(DefKind::Fn, _) => {
+ // Verify whether this is a fn call or an Fn used as a type.
+ self.r.session.source_map().span_to_snippet(span).map(|snippet| {
+ snippet.ends_with(')')
+ }).unwrap_or(false)
+ }
+ Res::Def(DefKind::Ctor(..), _) |
+ Res::Def(DefKind::Method, _) |
+ Res::Def(DefKind::Const, _) |
+ Res::Def(DefKind::AssocConst, _) |
+ Res::SelfCtor(_) |
+ Res::PrimTy(_) |
+ Res::Local(_) => true,
+ _ => false,
+ })
} else {
let item_span = path.last().unwrap().ident.span;
let (mod_prefix, mod_str) = if path.len() == 1 {
};
(format!("cannot find {} `{}` in {}{}", expected, item_str, mod_prefix, mod_str),
format!("not found in {}", mod_str),
- item_span)
+ item_span,
+ false)
};
let code = DiagnosticId::Error(code.into());
"`self` value is a keyword only available in methods with a `self` parameter",
),
});
- if let Some(span) = &self.current_function {
+ if let Some(span) = &self.diagnostic_metadata.current_function {
err.span_label(*span, "this function doesn't have a `self` parameter");
}
return (err, Vec::new());
if !levenshtein_worked {
err.span_label(base_span, fallback_label);
self.type_ascription_suggestion(&mut err, base_span);
+ match self.diagnostic_metadata.current_let_binding {
+ Some((pat_sp, Some(ty_sp), None))
+ if ty_sp.contains(base_span) && could_be_expr => {
+ err.span_suggestion_short(
+ pat_sp.between(ty_sp),
+ "use `=` if you meant to assign",
+ " = ".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ _ => {}
+ }
}
(err, candidates)
}
// Fields are generally expected in the same contexts as locals.
if filter_fn(Res::Local(ast::DUMMY_NODE_ID)) {
- if let Some(node_id) = self.current_self_type.as_ref().and_then(extract_node_id) {
+ if let Some(node_id) = self.diagnostic_metadata.current_self_type.as_ref()
+ .and_then(extract_node_id)
+ {
// Look for a field with the same name in the current self_type.
if let Some(resolution) = self.r.partial_res_map.get(&node_id) {
match resolution.base_res() {
}
}
- for assoc_type_ident in &self.current_trait_assoc_types {
+ for assoc_type_ident in &self.diagnostic_metadata.current_trait_assoc_types {
if *assoc_type_ident == ident {
return Some(AssocSuggestion::AssocItem);
}
err: &mut DiagnosticBuilder<'_>,
base_span: Span,
) {
- debug!("type_ascription_suggetion {:?}", base_span);
let cm = self.r.session.source_map();
let base_snippet = cm.span_to_snippet(base_span);
- debug!("self.current_type_ascription {:?}", self.current_type_ascription);
- if let Some(sp) = self.current_type_ascription.last() {
+ if let Some(sp) = self.diagnostic_metadata.current_type_ascription.last() {
let mut sp = *sp;
loop {
// Try to find the `:`; bail on first non-':' / non-whitespace.
//! Type-checking for the rust-intrinsic and platform-intrinsic
//! intrinsics that the compiler exposes.
+use rustc::middle::lang_items::PanicLocationLangItem;
use rustc::traits::{ObligationCause, ObligationCauseCode};
use rustc::ty::{self, TyCtxt, Ty};
use rustc::ty::subst::Subst;
/// Returns `true` if the given intrinsic is unsafe to call or not.
pub fn intrinsic_operation_unsafety(intrinsic: &str) -> hir::Unsafety {
match intrinsic {
- "size_of" | "min_align_of" | "needs_drop" |
+ "size_of" | "min_align_of" | "needs_drop" | "caller_location" |
"add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" |
"wrapping_add" | "wrapping_sub" | "wrapping_mul" |
"saturating_add" | "saturating_sub" |
], tcx.types.usize)
}
"rustc_peek" => (1, vec![param(0)], param(0)),
+ "caller_location" => (
+ 0,
+ vec![],
+ tcx.mk_imm_ref(
+ tcx.lifetimes.re_static,
+ tcx.type_of(tcx.require_lang_item(PanicLocationLangItem, None))
+ .subst(tcx, tcx.mk_substs([tcx.lifetimes.re_static.into()].iter())),
+ ),
+ ),
"panic_if_uninhabited" => (1, Vec::new(), tcx.mk_unit()),
"init" => (1, Vec::new(), param(0)),
"uninit" => (1, Vec::new(), param(0)),
let mut skip_add = false;
if let ty::Opaque(defin_ty_def_id, _substs) = definition_ty.kind {
- if def_id == defin_ty_def_id {
- debug!("Skipping adding concrete definition for opaque type {:?} {:?}",
- opaque_defn, defin_ty_def_id);
- skip_add = true;
+ if let hir::OpaqueTyOrigin::TypeAlias = opaque_defn.origin {
+ if def_id == defin_ty_def_id {
+ debug!("Skipping adding concrete definition for opaque type {:?} {:?}",
+ opaque_defn, defin_ty_def_id);
+ skip_add = true;
+ }
}
}
);
let inferred_outlives = tcx.inferred_outlives_of(def_id);
if !inferred_outlives.is_empty() {
- let span = tcx.def_span(def_id);
debug!(
"predicates_defined_on: inferred_outlives_of({:?}) = {:?}",
def_id,
inferred_outlives,
);
- result.predicates = tcx.arena.alloc_from_iter(
- result.predicates.iter().copied().chain(
- // FIXME(eddyb) use better spans - maybe add `Span`s
- // to `inferred_outlives_of` predicates as well?
- inferred_outlives.iter().map(|&p| (p, span)),
- ),
- );
+ if result.predicates.is_empty() {
+ result.predicates = inferred_outlives;
+ } else {
+ result.predicates = tcx.arena.alloc_from_iter(
+ result.predicates.iter().chain(inferred_outlives).copied(),
+ );
+ }
}
debug!("predicates_defined_on({:?}) = {:?}", def_id, result);
result
let mut required_predicates = RequiredPredicates::default();
// process predicates and convert to `RequiredPredicates` entry, see below
- for (pred, _) in predicates.predicates {
- match pred {
+ for &(predicate, span) in predicates.predicates {
+ match predicate {
ty::Predicate::TypeOutlives(predicate) => {
let OutlivesPredicate(ref ty, ref reg) = predicate.skip_binder();
- insert_outlives_predicate(tcx, (*ty).into(), reg, &mut required_predicates)
+ insert_outlives_predicate(
+ tcx,
+ (*ty).into(),
+ reg,
+ span,
+ &mut required_predicates,
+ )
}
ty::Predicate::RegionOutlives(predicate) => {
tcx,
(*reg1).into(),
reg2,
+ span,
&mut required_predicates,
)
}
use rustc::ty::subst::{GenericArg, Subst, GenericArgKind};
use rustc::ty::{self, Ty, TyCtxt};
use rustc::util::nodemap::FxHashMap;
+use syntax_pos::Span;
use super::explicit::ExplicitPredicatesMap;
use super::utils::*;
// (struct/enum/union) there will be outlive
// requirements for adt_def.
let field_ty = self.tcx.type_of(field_def.did);
+ let field_span = self.tcx.def_span(field_def.did);
insert_required_predicates_to_be_wf(
self.tcx,
field_ty,
+ field_span,
self.global_inferred_outlives,
&mut item_required_predicates,
&mut self.explicit_map,
fn insert_required_predicates_to_be_wf<'tcx>(
tcx: TyCtxt<'tcx>,
field_ty: Ty<'tcx>,
+ field_span: Span,
global_inferred_outlives: &FxHashMap<DefId, RequiredPredicates<'tcx>>,
required_predicates: &mut RequiredPredicates<'tcx>,
explicit_map: &mut ExplicitPredicatesMap<'tcx>,
// We also want to calculate potential predicates for the T
ty::Ref(region, rty, _) => {
debug!("Ref");
- insert_outlives_predicate(tcx, rty.into(), region, required_predicates);
+ insert_outlives_predicate(tcx, rty.into(), region, field_span, required_predicates);
}
// For each Adt (struct/enum/union) type `Foo<'a, T>`, we
// 'a` holds for `Foo`.
debug!("Adt");
if let Some(unsubstituted_predicates) = global_inferred_outlives.get(&def.did) {
- for unsubstituted_predicate in unsubstituted_predicates {
+ for (unsubstituted_predicate, &span) in unsubstituted_predicates {
// `unsubstituted_predicate` is `U: 'b` in the
// example above. So apply the substitution to
// get `T: 'a` (or `predicate`):
tcx,
predicate.0,
predicate.1,
+ span,
required_predicates,
);
}
);
let explicit_predicates = explicit_map.explicit_predicates_of(tcx, def_id);
- for outlives_predicate in explicit_predicates.iter() {
+ for (outlives_predicate, &span) in explicit_predicates {
debug!("outlives_predicate = {:?}", &outlives_predicate);
// Careful: If we are inferring the effects of a `dyn Trait<..>`
let predicate = outlives_predicate.subst(tcx, substs);
debug!("predicate = {:?}", &predicate);
- insert_outlives_predicate(tcx, predicate.0.into(), predicate.1, required_predicates);
+ insert_outlives_predicate(tcx, predicate.0.into(), predicate.1, span, required_predicates);
}
}
use rustc::ty::subst::GenericArgKind;
use rustc::ty::{self, CratePredicatesMap, TyCtxt};
use syntax::symbol::sym;
+use syntax_pos::Span;
mod explicit;
mod implicit_infer;
fn inferred_outlives_of(
tcx: TyCtxt<'_>,
item_def_id: DefId,
-) -> &[ty::Predicate<'_>] {
+) -> &[(ty::Predicate<'_>, Span)] {
let id = tcx
.hir()
.as_local_hir_id(item_def_id)
if tcx.has_attr(item_def_id, sym::rustc_outlives) {
let mut pred: Vec<String> = predicates
.iter()
- .map(|out_pred| match out_pred {
+ .map(|(out_pred, _)| match out_pred {
ty::Predicate::RegionOutlives(p) => p.to_string(),
ty::Predicate::TypeOutlives(p) => p.to_string(),
err => bug!("unexpected predicate {:?}", err),
let predicates = global_inferred_outlives
.iter()
.map(|(&def_id, set)| {
- let predicates = tcx.arena.alloc_from_iter(set
+ let predicates = &*tcx.arena.alloc_from_iter(set
.iter()
.filter_map(
- |ty::OutlivesPredicate(kind1, region2)| match kind1.unpack() {
+ |(ty::OutlivesPredicate(kind1, region2), &span)| match kind1.unpack() {
GenericArgKind::Type(ty1) => {
- Some(ty::Predicate::TypeOutlives(ty::Binder::bind(
+ Some((ty::Predicate::TypeOutlives(ty::Binder::bind(
ty::OutlivesPredicate(ty1, region2)
- )))
+ )), span))
}
GenericArgKind::Lifetime(region1) => {
- Some(ty::Predicate::RegionOutlives(
+ Some((ty::Predicate::RegionOutlives(
ty::Binder::bind(ty::OutlivesPredicate(region1, region2))
- ))
+ ), span))
}
GenericArgKind::Const(_) => {
// Generic consts don't impose any constraints.
}
},
));
- (def_id, &*predicates)
+ (def_id, predicates)
}).collect();
tcx.arena.alloc(ty::CratePredicatesMap {
use rustc::ty::subst::{GenericArg, GenericArgKind};
use rustc::ty::{self, Region, RegionKind, Ty, TyCtxt};
use smallvec::smallvec;
-use std::collections::BTreeSet;
+use std::collections::BTreeMap;
+use syntax_pos::Span;
/// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred
/// must be added to the struct header.
pub type RequiredPredicates<'tcx> =
- BTreeSet<ty::OutlivesPredicate<GenericArg<'tcx>, ty::Region<'tcx>>>;
+ BTreeMap<ty::OutlivesPredicate<GenericArg<'tcx>, ty::Region<'tcx>>, Span>;
/// Given a requirement `T: 'a` or `'b: 'a`, deduce the
/// outlives_component and add it to `required_predicates`
tcx: TyCtxt<'tcx>,
kind: GenericArg<'tcx>,
outlived_region: Region<'tcx>,
+ span: Span,
required_predicates: &mut RequiredPredicates<'tcx>,
) {
// If the `'a` region is bound within the field type itself, we
tcx,
r.into(),
outlived_region,
+ span,
required_predicates,
);
}
// where clause that `U: 'a`.
let ty: Ty<'tcx> = param_ty.to_ty(tcx);
required_predicates
- .insert(ty::OutlivesPredicate(ty.into(), outlived_region));
+ .entry(ty::OutlivesPredicate(ty.into(), outlived_region))
+ .or_insert(span);
}
Component::Projection(proj_ty) => {
// Here we want to add an explicit `where <T as Iterator>::Item: 'a`.
let ty: Ty<'tcx> = tcx.mk_projection(proj_ty.item_def_id, proj_ty.substs);
required_predicates
- .insert(ty::OutlivesPredicate(ty.into(), outlived_region));
+ .entry(ty::OutlivesPredicate(ty.into(), outlived_region))
+ .or_insert(span);
}
Component::EscapingProjection(_) => {
if !is_free_region(tcx, r) {
return;
}
- required_predicates.insert(ty::OutlivesPredicate(kind, outlived_region));
+ required_predicates.entry(ty::OutlivesPredicate(kind, outlived_region))
+ .or_insert(span);
}
GenericArgKind::Const(_) => {
}
let (file, line, col) = *file_line_col;
- let info = PanicInfo::internal_constructor(
- Some(msg),
- Location::internal_constructor(file, line, col),
- );
+ let location = Location::internal_constructor(file, line, col);
+ let info = PanicInfo::internal_constructor(Some(msg), &location);
continue_panic_fmt(&info)
}
}
unsafe {
- let mut info = PanicInfo::internal_constructor(
- message,
- Location::internal_constructor(file, line, col),
- );
+ let location = Location::internal_constructor(file, line, col);
+ let mut info = PanicInfo::internal_constructor(message, &location);
HOOK_LOCK.read();
match HOOK {
// Some platforms know that printing to stderr won't ever actually
err.span_suggestion_short(
colon_sp,
"use `=` if you meant to assign",
- "=".to_string(),
+ " =".to_string(),
Applicability::MachineApplicable
);
err.emit();
--- /dev/null
+#![feature(fundamental)]
+#![feature(re_rebalance_coherence)]
+
+// compile-flags:--crate-name=test
+// aux-build:coherence_lib.rs
+// check-pass
+
+extern crate coherence_lib as lib;
+use lib::*;
+
+#[fundamental]
+struct Local;
+
+impl Remote for Local {}
+
+fn main() {}
--- /dev/null
+#![feature(fundamental)]
+#![feature(re_rebalance_coherence)]
+
+// compile-flags:--crate-name=test
+// aux-build:coherence_lib.rs
+// check-pass
+
+extern crate coherence_lib as lib;
+use lib::*;
+
+#[fundamental]
+struct MyBox<T>(T);
+
+impl<T> Remote for MyBox<T> {}
+
+fn main() {}
--- /dev/null
+// run-pass
+
+#![feature(const_fn, core_intrinsics)]
+
+use std::{intrinsics::caller_location, panic::Location};
+
+const LOCATION: &Location = caller_location();
+const NESTED: &Location = {
+ const fn nested_location() -> &'static Location<'static> {
+ caller_location()
+ };
+ nested_location()
+};
+
+fn main() {
+ assert_eq!(LOCATION.file(), file!());
+ assert_eq!(LOCATION.line(), 7);
+ assert_eq!(LOCATION.column(), 29);
+
+ assert_eq!(NESTED.file(), file!());
+ assert_eq!(NESTED.line(), 10);
+ assert_eq!(NESTED.column(), 9);
+}
//[thin]compile-flags: -C lto=thin
//[fat]compile-flags: -C lto=fat
-#![feature(core_panic)]
+#![feature(core_panic, panic_internals)]
// (For some reason, reproducing the LTO issue requires pulling in std
// explicitly this way.)
let _guard = Droppable;
let s = "issue-64655-allow-unwind-when-calling-panic-directly.rs";
- core::panicking::panic(&("???", s, 17, 4));
+ let location = core::panic::Location::internal_constructor(s, 17, 4);
+ core::panicking::panic("???", &location);
});
let wait = handle.join();
error: aborting due to 3 previous errors
+For more information about this error, try `rustc --explain E0666`.
+++ /dev/null
-// Test that impl trait does not allow creating recursive types that are
-// otherwise forbidden. Even when there's an opaque type in another crate
-// hiding this.
-
-fn id<T>(t: T) -> impl Sized { t }
-
-fn recursive_id() -> impl Sized { //~ ERROR opaque type expands to a recursive type
- id(recursive_id2())
-}
-
-fn recursive_id2() -> impl Sized { //~ ERROR opaque type expands to a recursive type
- id(recursive_id())
-}
-
-fn wrap<T>(t: T) -> impl Sized { (t,) }
-
-fn recursive_wrap() -> impl Sized { //~ ERROR opaque type expands to a recursive type
- wrap(recursive_wrap2())
-}
-
-fn recursive_wrap2() -> impl Sized { //~ ERROR opaque type expands to a recursive type
- wrap(recursive_wrap())
-}
-
-fn main() {}
+++ /dev/null
-error[E0720]: opaque type expands to a recursive type
- --> $DIR/recursive-impl-trait-type--through-non-recursize.rs:7:22
- |
-LL | fn recursive_id() -> impl Sized {
- | ^^^^^^^^^^ expands to a recursive type
- |
- = note: type resolves to itself
-
-error[E0720]: opaque type expands to a recursive type
- --> $DIR/recursive-impl-trait-type--through-non-recursize.rs:11:23
- |
-LL | fn recursive_id2() -> impl Sized {
- | ^^^^^^^^^^ expands to a recursive type
- |
- = note: type resolves to itself
-
-error[E0720]: opaque type expands to a recursive type
- --> $DIR/recursive-impl-trait-type--through-non-recursize.rs:17:24
- |
-LL | fn recursive_wrap() -> impl Sized {
- | ^^^^^^^^^^ expands to a recursive type
- |
- = note: expanded type is `((impl Sized,),)`
-
-error[E0720]: opaque type expands to a recursive type
- --> $DIR/recursive-impl-trait-type--through-non-recursize.rs:21:25
- |
-LL | fn recursive_wrap2() -> impl Sized {
- | ^^^^^^^^^^ expands to a recursive type
- |
- = note: expanded type is `((impl Sized,),)`
-
-error: aborting due to 4 previous errors
-
-For more information about this error, try `rustc --explain E0720`.
--- /dev/null
+// Test that an `impl Trait` type that expands to itself is an error.
+
+fn test() -> impl Sized { //~ ERROR E0720
+ test()
+}
+
+fn main() {}
--- /dev/null
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-direct.rs:3:14
+ |
+LL | fn test() -> impl Sized {
+ | ^^^^^^^^^^ expands to a recursive type
+ |
+ = note: type resolves to itself
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0720`.
--- /dev/null
+// Test that impl trait does not allow creating recursive types that are
+// otherwise forbidden.
+
+#![feature(generators)]
+
+fn option(i: i32) -> impl Sized { //~ ERROR
+ if i < 0 {
+ None
+ } else {
+ Some((option(i - 1), i))
+ }
+}
+
+fn tuple() -> impl Sized { //~ ERROR
+ (tuple(),)
+}
+
+fn array() -> impl Sized { //~ ERROR
+ [array()]
+}
+
+fn ptr() -> impl Sized { //~ ERROR
+ &ptr() as *const _
+}
+
+fn fn_ptr() -> impl Sized { //~ ERROR
+ fn_ptr as fn() -> _
+}
+
+fn closure_capture() -> impl Sized { //~ ERROR
+ let x = closure_capture();
+ move || { x; }
+}
+
+fn closure_ref_capture() -> impl Sized { //~ ERROR
+ let x = closure_ref_capture();
+ move || { &x; }
+}
+
+fn closure_sig() -> impl Sized { //~ ERROR
+ || closure_sig()
+}
+
+fn generator_sig() -> impl Sized { //~ ERROR
+ || generator_sig()
+}
+
+fn generator_capture() -> impl Sized { //~ ERROR
+ let x = generator_capture();
+ move || { yield; x; }
+}
+
+fn substs_change<T>() -> impl Sized { //~ ERROR
+ (substs_change::<&T>(),)
+}
+
+fn generator_hold() -> impl Sized { //~ ERROR
+ move || {
+ let x = generator_hold();
+ yield;
+ x;
+ }
+}
+
+fn use_fn_ptr() -> impl Sized { // OK, error already reported
+ fn_ptr()
+}
+
+fn mutual_recursion() -> impl Sync { //~ ERROR
+ mutual_recursion_b()
+}
+
+fn mutual_recursion_b() -> impl Sized { //~ ERROR
+ mutual_recursion()
+}
+
+fn main() {}
--- /dev/null
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:6:22
+ |
+LL | fn option(i: i32) -> impl Sized {
+ | ^^^^^^^^^^ expands to a recursive type
+ |
+ = note: expanded type is `std::option::Option<(impl Sized, i32)>`
+
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:14:15
+ |
+LL | fn tuple() -> impl Sized {
+ | ^^^^^^^^^^ expands to a recursive type
+ |
+ = note: expanded type is `(impl Sized,)`
+
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:18:15
+ |
+LL | fn array() -> impl Sized {
+ | ^^^^^^^^^^ expands to a recursive type
+ |
+ = note: expanded type is `[impl Sized; 1]`
+
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:22:13
+ |
+LL | fn ptr() -> impl Sized {
+ | ^^^^^^^^^^ expands to a recursive type
+ |
+ = note: expanded type is `*const impl Sized`
+
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:26:16
+ |
+LL | fn fn_ptr() -> impl Sized {
+ | ^^^^^^^^^^ expands to a recursive type
+ |
+ = note: expanded type is `fn() -> impl Sized`
+
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:30:25
+ |
+LL | fn closure_capture() -> impl Sized {
+ | ^^^^^^^^^^ expands to a recursive type
+ |
+ = note: expanded type is `[closure@$DIR/recursive-impl-trait-type-indirect.rs:32:5: 32:19 x:impl Sized]`
+
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:35:29
+ |
+LL | fn closure_ref_capture() -> impl Sized {
+ | ^^^^^^^^^^ expands to a recursive type
+ |
+ = note: expanded type is `[closure@$DIR/recursive-impl-trait-type-indirect.rs:37:5: 37:20 x:impl Sized]`
+
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:40:21
+ |
+LL | fn closure_sig() -> impl Sized {
+ | ^^^^^^^^^^ expands to a recursive type
+ |
+ = note: expanded type is `[closure@$DIR/recursive-impl-trait-type-indirect.rs:41:5: 41:21]`
+
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:44:23
+ |
+LL | fn generator_sig() -> impl Sized {
+ | ^^^^^^^^^^ expands to a recursive type
+ |
+ = note: expanded type is `[closure@$DIR/recursive-impl-trait-type-indirect.rs:45:5: 45:23]`
+
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:48:27
+ |
+LL | fn generator_capture() -> impl Sized {
+ | ^^^^^^^^^^ expands to a recursive type
+ |
+ = note: expanded type is `[generator@$DIR/recursive-impl-trait-type-indirect.rs:50:5: 50:26 x:impl Sized {()}]`
+
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:53:26
+ |
+LL | fn substs_change<T>() -> impl Sized {
+ | ^^^^^^^^^^ expands to a recursive type
+ |
+ = note: expanded type is `(impl Sized,)`
+
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:57:24
+ |
+LL | fn generator_hold() -> impl Sized {
+ | ^^^^^^^^^^ expands to a recursive type
+ |
+ = note: expanded type is `[generator@$DIR/recursive-impl-trait-type-indirect.rs:58:5: 62:6 {impl Sized, ()}]`
+
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:69:26
+ |
+LL | fn mutual_recursion() -> impl Sync {
+ | ^^^^^^^^^ expands to a recursive type
+ |
+ = note: type resolves to itself
+
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:73:28
+ |
+LL | fn mutual_recursion_b() -> impl Sized {
+ | ^^^^^^^^^^ expands to a recursive type
+ |
+ = note: type resolves to itself
+
+error: aborting due to 14 previous errors
+
+For more information about this error, try `rustc --explain E0720`.
--- /dev/null
+// Test that impl trait does not allow creating recursive types that are
+// otherwise forbidden. Even when there's an opaque type in another crate
+// hiding this.
+
+fn id<T>(t: T) -> impl Sized { t }
+
+fn recursive_id() -> impl Sized { //~ ERROR opaque type expands to a recursive type
+ id(recursive_id2())
+}
+
+fn recursive_id2() -> impl Sized { //~ ERROR opaque type expands to a recursive type
+ id(recursive_id())
+}
+
+fn wrap<T>(t: T) -> impl Sized { (t,) }
+
+fn recursive_wrap() -> impl Sized { //~ ERROR opaque type expands to a recursive type
+ wrap(recursive_wrap2())
+}
+
+fn recursive_wrap2() -> impl Sized { //~ ERROR opaque type expands to a recursive type
+ wrap(recursive_wrap())
+}
+
+fn main() {}
--- /dev/null
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-through-non-recursive.rs:7:22
+ |
+LL | fn recursive_id() -> impl Sized {
+ | ^^^^^^^^^^ expands to a recursive type
+ |
+ = note: type resolves to itself
+
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-through-non-recursive.rs:11:23
+ |
+LL | fn recursive_id2() -> impl Sized {
+ | ^^^^^^^^^^ expands to a recursive type
+ |
+ = note: type resolves to itself
+
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-through-non-recursive.rs:17:24
+ |
+LL | fn recursive_wrap() -> impl Sized {
+ | ^^^^^^^^^^ expands to a recursive type
+ |
+ = note: expanded type is `((impl Sized,),)`
+
+error[E0720]: opaque type expands to a recursive type
+ --> $DIR/recursive-impl-trait-type-through-non-recursive.rs:21:25
+ |
+LL | fn recursive_wrap2() -> impl Sized {
+ | ^^^^^^^^^^ expands to a recursive type
+ |
+ = note: expanded type is `((impl Sized,),)`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0720`.
+++ /dev/null
-// Test that impl trait does not allow creating recursive types that are
-// otherwise forbidden.
-
-#![feature(generators)]
-
-fn option(i: i32) -> impl Sized { //~ ERROR
- if i < 0 {
- None
- } else {
- Some((option(i - 1), i))
- }
-}
-
-fn tuple() -> impl Sized { //~ ERROR
- (tuple(),)
-}
-
-fn array() -> impl Sized { //~ ERROR
- [array()]
-}
-
-fn ptr() -> impl Sized { //~ ERROR
- &ptr() as *const _
-}
-
-fn fn_ptr() -> impl Sized { //~ ERROR
- fn_ptr as fn() -> _
-}
-
-fn closure_capture() -> impl Sized { //~ ERROR
- let x = closure_capture();
- move || { x; }
-}
-
-fn closure_ref_capture() -> impl Sized { //~ ERROR
- let x = closure_ref_capture();
- move || { &x; }
-}
-
-fn closure_sig() -> impl Sized { //~ ERROR
- || closure_sig()
-}
-
-fn generator_sig() -> impl Sized { //~ ERROR
- || generator_sig()
-}
-
-fn generator_capture() -> impl Sized { //~ ERROR
- let x = generator_capture();
- move || { yield; x; }
-}
-
-fn substs_change<T>() -> impl Sized { //~ ERROR
- (substs_change::<&T>(),)
-}
-
-fn generator_hold() -> impl Sized { //~ ERROR
- move || {
- let x = generator_hold();
- yield;
- x;
- }
-}
-
-fn use_fn_ptr() -> impl Sized { // OK, error already reported
- fn_ptr()
-}
-
-fn mutual_recursion() -> impl Sync { //~ ERROR
- mutual_recursion_b()
-}
-
-fn mutual_recursion_b() -> impl Sized { //~ ERROR
- mutual_recursion()
-}
-
-fn main() {}
+++ /dev/null
-error[E0720]: opaque type expands to a recursive type
- --> $DIR/recursive-impl-trait-type.rs:6:22
- |
-LL | fn option(i: i32) -> impl Sized {
- | ^^^^^^^^^^ expands to a recursive type
- |
- = note: expanded type is `std::option::Option<(impl Sized, i32)>`
-
-error[E0720]: opaque type expands to a recursive type
- --> $DIR/recursive-impl-trait-type.rs:14:15
- |
-LL | fn tuple() -> impl Sized {
- | ^^^^^^^^^^ expands to a recursive type
- |
- = note: expanded type is `(impl Sized,)`
-
-error[E0720]: opaque type expands to a recursive type
- --> $DIR/recursive-impl-trait-type.rs:18:15
- |
-LL | fn array() -> impl Sized {
- | ^^^^^^^^^^ expands to a recursive type
- |
- = note: expanded type is `[impl Sized; 1]`
-
-error[E0720]: opaque type expands to a recursive type
- --> $DIR/recursive-impl-trait-type.rs:22:13
- |
-LL | fn ptr() -> impl Sized {
- | ^^^^^^^^^^ expands to a recursive type
- |
- = note: expanded type is `*const impl Sized`
-
-error[E0720]: opaque type expands to a recursive type
- --> $DIR/recursive-impl-trait-type.rs:26:16
- |
-LL | fn fn_ptr() -> impl Sized {
- | ^^^^^^^^^^ expands to a recursive type
- |
- = note: expanded type is `fn() -> impl Sized`
-
-error[E0720]: opaque type expands to a recursive type
- --> $DIR/recursive-impl-trait-type.rs:30:25
- |
-LL | fn closure_capture() -> impl Sized {
- | ^^^^^^^^^^ expands to a recursive type
- |
- = note: expanded type is `[closure@$DIR/recursive-impl-trait-type.rs:32:5: 32:19 x:impl Sized]`
-
-error[E0720]: opaque type expands to a recursive type
- --> $DIR/recursive-impl-trait-type.rs:35:29
- |
-LL | fn closure_ref_capture() -> impl Sized {
- | ^^^^^^^^^^ expands to a recursive type
- |
- = note: expanded type is `[closure@$DIR/recursive-impl-trait-type.rs:37:5: 37:20 x:impl Sized]`
-
-error[E0720]: opaque type expands to a recursive type
- --> $DIR/recursive-impl-trait-type.rs:40:21
- |
-LL | fn closure_sig() -> impl Sized {
- | ^^^^^^^^^^ expands to a recursive type
- |
- = note: expanded type is `[closure@$DIR/recursive-impl-trait-type.rs:41:5: 41:21]`
-
-error[E0720]: opaque type expands to a recursive type
- --> $DIR/recursive-impl-trait-type.rs:44:23
- |
-LL | fn generator_sig() -> impl Sized {
- | ^^^^^^^^^^ expands to a recursive type
- |
- = note: expanded type is `[closure@$DIR/recursive-impl-trait-type.rs:45:5: 45:23]`
-
-error[E0720]: opaque type expands to a recursive type
- --> $DIR/recursive-impl-trait-type.rs:48:27
- |
-LL | fn generator_capture() -> impl Sized {
- | ^^^^^^^^^^ expands to a recursive type
- |
- = note: expanded type is `[generator@$DIR/recursive-impl-trait-type.rs:50:5: 50:26 x:impl Sized {()}]`
-
-error[E0720]: opaque type expands to a recursive type
- --> $DIR/recursive-impl-trait-type.rs:53:26
- |
-LL | fn substs_change<T>() -> impl Sized {
- | ^^^^^^^^^^ expands to a recursive type
- |
- = note: expanded type is `(impl Sized,)`
-
-error[E0720]: opaque type expands to a recursive type
- --> $DIR/recursive-impl-trait-type.rs:57:24
- |
-LL | fn generator_hold() -> impl Sized {
- | ^^^^^^^^^^ expands to a recursive type
- |
- = note: expanded type is `[generator@$DIR/recursive-impl-trait-type.rs:58:5: 62:6 {impl Sized, ()}]`
-
-error[E0720]: opaque type expands to a recursive type
- --> $DIR/recursive-impl-trait-type.rs:69:26
- |
-LL | fn mutual_recursion() -> impl Sync {
- | ^^^^^^^^^ expands to a recursive type
- |
- = note: type resolves to itself
-
-error[E0720]: opaque type expands to a recursive type
- --> $DIR/recursive-impl-trait-type.rs:73:28
- |
-LL | fn mutual_recursion_b() -> impl Sized {
- | ^^^^^^^^^^ expands to a recursive type
- |
- = note: type resolves to itself
-
-error: aborting due to 14 previous errors
-
-For more information about this error, try `rustc --explain E0720`.
error: aborting due to 43 previous errors
-Some errors have detailed explanations: E0282, E0562, E0658.
+Some errors have detailed explanations: E0282, E0562, E0658, E0666.
For more information about an error, try `rustc --explain E0282`.
error: aborting due to 6 previous errors
-For more information about this error, try `rustc --explain E0562`.
+Some errors have detailed explanations: E0562, E0666.
+For more information about an error, try `rustc --explain E0562`.
use foo2::Bar;
let _x : Box<Bar>; //~ ERROR expected type, found function `Bar`
+ let _x : Bar(); //~ ERROR expected type, found function `Bar`
}
fn test_list2() {
|
error[E0573]: expected type, found function `Bar`
- --> $DIR/privacy-ns2.rs:47:17
+ --> $DIR/privacy-ns2.rs:42:14
+ |
+LL | let _x : Bar();
+ | ^^^^^ not a type
+ |
+help: use `=` if you meant to assign
+ |
+LL | let _x = Bar();
+ | ^
+help: possible better candidates are found in other modules, you can import them into scope
+ |
+LL | use foo1::Bar;
+ |
+LL | use foo2::Bar;
+ |
+LL | use foo3::Bar;
+ |
+
+error[E0573]: expected type, found function `Bar`
+ --> $DIR/privacy-ns2.rs:48:17
|
LL | let _x: Box<Bar>;
| ^^^
|
error[E0603]: trait `Bar` is private
- --> $DIR/privacy-ns2.rs:60:15
+ --> $DIR/privacy-ns2.rs:61:15
|
LL | use foo3::Bar;
| ^^^
error[E0603]: trait `Bar` is private
- --> $DIR/privacy-ns2.rs:64:15
+ --> $DIR/privacy-ns2.rs:65:15
|
LL | use foo3::Bar;
| ^^^
error[E0603]: trait `Bar` is private
- --> $DIR/privacy-ns2.rs:71:16
+ --> $DIR/privacy-ns2.rs:72:16
|
LL | use foo3::{Bar,Baz};
| ^^^
-error: aborting due to 7 previous errors
+error: aborting due to 8 previous errors
Some errors have detailed explanations: E0423, E0573, E0603.
For more information about an error, try `rustc --explain E0423`.
--- /dev/null
+// run-pass
+
+#![feature(core_intrinsics)]
+fn main() {
+ let loc = core::intrinsics::caller_location();
+ assert_eq!(loc.file(), file!());
+ assert_eq!(loc.line(), 5);
+ assert_eq!(loc.column(), 15);
+}
--- /dev/null
+pub fn foo(num: i32) -> i32 {
+ let foo: i32::from_be(num);
+ //~^ ERROR expected type, found local variable `num`
+ //~| ERROR parenthesized type parameters may only be used with a `Fn` trait
+ //~| ERROR ambiguous associated type
+ //~| WARNING this was previously accepted by the compiler but is being phased out
+ foo
+}
+
+fn main() {
+ let _ = foo(42);
+}
--- /dev/null
+error[E0573]: expected type, found local variable `num`
+ --> $DIR/let-binding-init-expr-as-ty.rs:2:27
+ |
+LL | let foo: i32::from_be(num);
+ | -- ^^^ not a type
+ | |
+ | help: use `=` if you meant to assign
+
+error: parenthesized type parameters may only be used with a `Fn` trait
+ --> $DIR/let-binding-init-expr-as-ty.rs:2:19
+ |
+LL | let foo: i32::from_be(num);
+ | ^^^^^^^^^^^^
+ |
+ = note: `#[deny(parenthesized_params_in_types_and_modules)]` on by default
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #42238 <https://github.com/rust-lang/rust/issues/42238>
+
+error[E0223]: ambiguous associated type
+ --> $DIR/let-binding-init-expr-as-ty.rs:2:14
+ |
+LL | let foo: i32::from_be(num);
+ | ^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<i32 as Trait>::from_be`
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0223, E0573.
+For more information about an error, try `rustc --explain E0223`.