From 3c78e9e3f6939befd3382c79fbe383bbe0ec894a Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Sun, 24 Jan 2021 15:48:27 +0100 Subject: [PATCH 01/11] add new Reveal for const eval during selection --- .../rustc_codegen_cranelift/src/constant.rs | 6 + compiler/rustc_codegen_ssa/src/mir/mod.rs | 5 +- .../rustc_middle/src/mir/interpret/error.rs | 5 +- compiler/rustc_middle/src/traits/mod.rs | 6 + compiler/rustc_middle/src/ty/consts/kind.rs | 2 +- compiler/rustc_middle/src/ty/mod.rs | 19 +- .../rustc_mir/src/const_eval/eval_queries.rs | 453 ++++++ .../rustc_mir/src/monomorphize/collector.rs | 1265 +++++++++++++++++ .../src/traits/const_evaluatable.rs | 3 +- .../src/traits/error_reporting/mod.rs | 3 +- .../src/traits/fulfill.rs | 5 + .../src/traits/project.rs | 3 +- .../src/traits/query/normalize.rs | 3 +- .../src/traits/select/mod.rs | 14 +- .../silent-selection-err.rs | 43 + 15 files changed, 1819 insertions(+), 16 deletions(-) create mode 100644 compiler/rustc_mir/src/const_eval/eval_queries.rs create mode 100644 compiler/rustc_mir/src/monomorphize/collector.rs create mode 100644 src/test/ui/const-generics/generic_const_exprs/silent-selection-err.rs diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 9a6c45ae98d5f..66679f6fe4696 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -64,6 +64,12 @@ pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool { err ); } + ErrorHandled::Silent => { + span_bug!( + constant.span, + "codgen encountered silent error", + ); + } } } } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 1ef863e84af7f..dbb5069d57d54 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -189,7 +189,10 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // errored or at least linted ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {} ErrorHandled::TooGeneric => { - span_bug!(const_.span, "codgen encountered polymorphic constant: {:?}", err) + span_bug!(const_.span, "codegen encountered polymorphic constant: {:?}", err) + } + ErrorHandled::Silent => { + span_bug!(const_.span, "silent error during codegen") } } } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index e9a857d09124f..714e7ba8ca9df 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -18,6 +18,9 @@ pub enum ErrorHandled { Reported(ErrorReported), /// Already emitted a lint for this evaluation. Linted, + /// Encountered an error without emitting anything. Only returned + /// with `Reveal::Selection`. + Silent, /// Don't emit an error, the evaluation failed because the MIR was generic /// and the substs didn't fully monomorphize it. TooGeneric, @@ -88,7 +91,7 @@ fn print_backtrace(backtrace: &Backtrace) { impl From for InterpErrorInfo<'_> { fn from(err: ErrorHandled) -> Self { match err { - ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => { + ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted | ErrorHandled::Silent => { err_inval!(ReferencedConstant) } ErrorHandled::TooGeneric => err_inval!(TooGeneric), diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index de5beffb5c541..c5a883d876818 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -37,6 +37,12 @@ pub use self::chalk::{ChalkEnvironmentAndGoal, RustInterner as ChalkRustInterner /// more or less conservative. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable)] pub enum Reveal { + // Similar to `Reveal::UserFacing`, except that we also do not emit errors + // when failing const evaluation. + // + // Used by `feature(const_evaluatable_checked)` to allow for `ConstEvaluatable` + // predicates to not hold without emitting an error. + Selection, /// At type-checking time, we refuse to project any associated /// type that is marked `default`. Non-`default` ("final") types /// are always projected. This is necessary in general for diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index 7188eed544551..d6204a8efb783 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -191,7 +191,7 @@ impl<'tcx> ConstKind<'tcx> { // (which may be identity substs, see above), // can leak through `val` into the const we return. Ok(val) => Some(Ok(val)), - Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => None, + Err(ErrorHandled::TooGeneric | ErrorHandled::Linted | ErrorHandled::Silent) => None, Err(ErrorHandled::Reported(e)) => Some(Err(e)), } } else { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 37d99766da9d2..72514e4456105 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -150,7 +150,11 @@ pub struct MainDefinition { impl MainDefinition { pub fn opt_fn_def_id(self) -> Option { - if let Res::Def(DefKind::Fn, def_id) = self.res { Some(def_id) } else { None } + if let Res::Def(DefKind::Fn, def_id) = self.res { + Some(def_id) + } else { + None + } } } @@ -1180,7 +1184,11 @@ impl WithOptConstParam { } pub fn def_id_for_type_of(self) -> DefId { - if let Some(did) = self.const_param_did { did } else { self.did.to_def_id() } + if let Some(did) = self.const_param_did { + did + } else { + self.did.to_def_id() + } } } @@ -1343,6 +1351,11 @@ impl<'tcx> ParamEnv<'tcx> { ty::ParamEnv { packed: CopyTaggedPtr::new(caller_bounds, ParamTag { reveal, constness }) } } + pub fn with_reveal_selection(mut self) -> Self { + self.packed.set_tag(Reveal::Selection); + self + } + pub fn with_user_facing(mut self) -> Self { self.packed.set_tag(ParamTag { reveal: Reveal::UserFacing, ..self.packed.tag() }); self @@ -1412,7 +1425,7 @@ impl<'tcx> ParamEnv<'tcx> { /// although the surrounding function is never reachable. pub fn and>(self, value: T) -> ParamEnvAnd<'tcx, T> { match self.reveal() { - Reveal::UserFacing => ParamEnvAnd { param_env: self, value }, + Reveal::Selection | Reveal::UserFacing => ParamEnvAnd { param_env: self, value }, Reveal::All => { if value.is_known_global() { diff --git a/compiler/rustc_mir/src/const_eval/eval_queries.rs b/compiler/rustc_mir/src/const_eval/eval_queries.rs new file mode 100644 index 0000000000000..303ebc829dc9f --- /dev/null +++ b/compiler/rustc_mir/src/const_eval/eval_queries.rs @@ -0,0 +1,453 @@ +use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr, MemoryExtra}; +use crate::interpret::eval_nullary_intrinsic; +use crate::interpret::{ + intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId, + Immediate, InternKind, InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, Scalar, + ScalarMaybeUninit, StackPopCleanup, +}; + +use rustc_errors::ErrorReported; +use rustc_hir::def::DefKind; +use rustc_middle::mir; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::traits::Reveal; +use rustc_middle::ty::layout::LayoutError; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::{self, subst::Subst, TyCtxt}; +use rustc_span::source_map::Span; +use rustc_target::abi::{Abi, LayoutOf}; +use std::convert::TryInto; + +pub fn note_on_undefined_behavior_error() -> &'static str { + "The rules on what exactly is undefined behavior aren't clear, \ + so this check might be overzealous. Please open an issue on the rustc \ + repository if you believe it should not be considered undefined behavior." +} + +// Returns a pointer to where the result lives +fn eval_body_using_ecx<'mir, 'tcx>( + ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, + cid: GlobalId<'tcx>, + body: &'mir mir::Body<'tcx>, +) -> InterpResult<'tcx, MPlaceTy<'tcx>> { + debug!("eval_body_using_ecx: {:?}, {:?}", cid, ecx.param_env); + let tcx = *ecx.tcx; + assert!( + cid.promoted.is_some() + || matches!( + ecx.tcx.def_kind(cid.instance.def_id()), + DefKind::Const + | DefKind::Static + | DefKind::ConstParam + | DefKind::AnonConst + | DefKind::AssocConst + ), + "Unexpected DefKind: {:?}", + ecx.tcx.def_kind(cid.instance.def_id()) + ); + let layout = ecx.layout_of(body.return_ty().subst(tcx, cid.instance.substs))?; + assert!(!layout.is_unsized()); + let ret = ecx.allocate(layout, MemoryKind::Stack); + + let name = + with_no_trimmed_paths(|| ty::tls::with(|tcx| tcx.def_path_str(cid.instance.def_id()))); + let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p)); + trace!("eval_body_using_ecx: pushing stack frame for global: {}{}", name, prom); + + ecx.push_stack_frame( + cid.instance, + body, + Some(ret.into()), + StackPopCleanup::None { cleanup: false }, + )?; + + // The main interpreter loop. + ecx.run()?; + + // Intern the result + let intern_kind = if cid.promoted.is_some() { + InternKind::Promoted + } else { + match tcx.static_mutability(cid.instance.def_id()) { + Some(m) => InternKind::Static(m), + None => InternKind::Constant, + } + }; + intern_const_alloc_recursive(ecx, intern_kind, ret)?; + + debug!("eval_body_using_ecx done: {:?}", *ret); + Ok(ret) +} + +/// The `InterpCx` is only meant to be used to do field and index projections into constants for +/// `simd_shuffle` and const patterns in match arms. +/// +/// The function containing the `match` that is currently being analyzed may have generic bounds +/// that inform us about the generic bounds of the constant. E.g., using an associated constant +/// of a function's generic parameter will require knowledge about the bounds on the generic +/// parameter. These bounds are passed to `mk_eval_cx` via the `ParamEnv` argument. +pub(super) fn mk_eval_cx<'mir, 'tcx>( + tcx: TyCtxt<'tcx>, + root_span: Span, + param_env: ty::ParamEnv<'tcx>, + can_access_statics: bool, +) -> CompileTimeEvalContext<'mir, 'tcx> { + debug!("mk_eval_cx: {:?}", param_env); + InterpCx::new( + tcx, + root_span, + param_env, + CompileTimeInterpreter::new(tcx.sess.const_eval_limit()), + MemoryExtra { can_access_statics }, + ) +} + +/// This function converts an interpreter value into a constant that is meant for use in the +/// type system. +pub(super) fn op_to_const<'tcx>( + ecx: &CompileTimeEvalContext<'_, 'tcx>, + op: OpTy<'tcx>, +) -> ConstValue<'tcx> { + // We do not have value optimizations for everything. + // Only scalars and slices, since they are very common. + // Note that further down we turn scalars of uninitialized bits back to `ByRef`. These can result + // from scalar unions that are initialized with one of their zero sized variants. We could + // instead allow `ConstValue::Scalar` to store `ScalarMaybeUninit`, but that would affect all + // the usual cases of extracting e.g. a `usize`, without there being a real use case for the + // `Undef` situation. + let try_as_immediate = match op.layout.abi { + Abi::Scalar(..) => true, + Abi::ScalarPair(..) => match op.layout.ty.kind() { + ty::Ref(_, inner, _) => match *inner.kind() { + ty::Slice(elem) => elem == ecx.tcx.types.u8, + ty::Str => true, + _ => false, + }, + _ => false, + }, + _ => false, + }; + let immediate = if try_as_immediate { + Err(ecx.read_immediate(op).expect("normalization works on validated constants")) + } else { + // It is guaranteed that any non-slice scalar pair is actually ByRef here. + // When we come back from raw const eval, we are always by-ref. The only way our op here is + // by-val is if we are in destructure_const, i.e., if this is (a field of) something that we + // "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or + // structs containing such. + op.try_as_mplace(ecx) + }; + + let to_const_value = |mplace: MPlaceTy<'_>| match mplace.ptr { + Scalar::Ptr(ptr) => { + let alloc = ecx.tcx.global_alloc(ptr.alloc_id).unwrap_memory(); + ConstValue::ByRef { alloc, offset: ptr.offset } + } + Scalar::Int(int) => { + assert!(mplace.layout.is_zst()); + assert_eq!( + int.assert_bits(ecx.tcx.data_layout.pointer_size) + % u128::from(mplace.layout.align.abi.bytes()), + 0, + "this MPlaceTy must come from a validated constant, thus we can assume the \ + alignment is correct", + ); + ConstValue::Scalar(Scalar::ZST) + } + }; + match immediate { + Ok(mplace) => to_const_value(mplace), + // see comment on `let try_as_immediate` above + Err(imm) => match *imm { + Immediate::Scalar(x) => match x { + ScalarMaybeUninit::Scalar(s) => ConstValue::Scalar(s), + ScalarMaybeUninit::Uninit => to_const_value(op.assert_mem_place(ecx)), + }, + Immediate::ScalarPair(a, b) => { + let (data, start) = match a.check_init().unwrap() { + Scalar::Ptr(ptr) => { + (ecx.tcx.global_alloc(ptr.alloc_id).unwrap_memory(), ptr.offset.bytes()) + } + Scalar::Int { .. } => ( + ecx.tcx + .intern_const_alloc(Allocation::from_byte_aligned_bytes(b"" as &[u8])), + 0, + ), + }; + let len = b.to_machine_usize(ecx).unwrap(); + let start = start.try_into().unwrap(); + let len: usize = len.try_into().unwrap(); + ConstValue::Slice { data, start, end: start + len } + } + }, + } +} + +fn turn_into_const_value<'tcx>( + tcx: TyCtxt<'tcx>, + constant: ConstAlloc<'tcx>, + key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, +) -> ConstValue<'tcx> { + let cid = key.value; + let def_id = cid.instance.def.def_id(); + let is_static = tcx.is_static(def_id); + let ecx = mk_eval_cx(tcx, tcx.def_span(key.value.instance.def_id()), key.param_env, is_static); + + let mplace = ecx.raw_const_to_mplace(constant).expect( + "can only fail if layout computation failed, \ + which should have given a good error before ever invoking this function", + ); + assert!( + !is_static || cid.promoted.is_some(), + "the `eval_to_const_value_raw` query should not be used for statics, use `eval_to_allocation` instead" + ); + // Turn this into a proper constant. + op_to_const(&ecx, mplace.into()) +} + +pub fn eval_to_const_value_raw_provider<'tcx>( + tcx: TyCtxt<'tcx>, + key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, +) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> { + // see comment in const_eval_raw_provider for what we're doing here + match key.param_env.reveal() { + Reveal::Selection => {} + Reveal::UserFacing => { + let mut key = key; + key.param_env = key.param_env.with_reveal_selection(); + match tcx.eval_to_const_value_raw(key) { + // try again with reveal all as requested + Err(ErrorHandled::Silent) => {} + // deduplicate calls + other => return other, + } + } + Reveal::All => { + let mut key = key; + key.param_env = key.param_env.with_user_facing(); + match tcx.eval_to_const_value_raw(key) { + // try again with reveal all as requested + Err(ErrorHandled::Silent) => bug!("unexpected error for {:?}", key), + Err(ErrorHandled::TooGeneric) => {} + // deduplicate calls + other => return other, + } + } + } + + // We call `const_eval` for zero arg intrinsics, too, in order to cache their value. + // Catch such calls and evaluate them instead of trying to load a constant's MIR. + if let ty::InstanceDef::Intrinsic(def_id) = key.value.instance.def { + let ty = key.value.instance.ty(tcx, key.param_env); + let substs = match ty.kind() { + ty::FnDef(_, substs) => substs, + _ => bug!("intrinsic with type {:?}", ty), + }; + return eval_nullary_intrinsic(tcx, key.param_env, def_id, substs).map_err(|error| { + let span = tcx.def_span(def_id); + let error = ConstEvalErr { error: error.kind, stacktrace: vec![], span }; + error.report_as_error(tcx.at(span), "could not evaluate nullary intrinsic") + }); + } + + tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key)) +} + +pub fn eval_to_allocation_raw_provider<'tcx>( + tcx: TyCtxt<'tcx>, + key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, +) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> { + // Because the constant is computed twice (once per value of `Reveal`), we are at risk of + // reporting the same error twice here. To resolve this, we check whether we can evaluate the + // constant in the more restrictive `Reveal::UserFacing`, which most likely already was + // computed. For a large percentage of constants that will already have succeeded. Only + // associated constants of generic functions will fail due to not enough monomorphization + // information being available. + match key.param_env.reveal() { + Reveal::Selection => {} + Reveal::UserFacing => { + let mut key = key; + key.param_env = key.param_env.with_reveal_selection(); + match tcx.eval_to_allocation_raw(key) { + Err(ErrorHandled::Silent) => {} + // deduplicate calls + other => return other, + } + } + Reveal::All => { + let mut key = key; + key.param_env = key.param_env.with_user_facing(); + match tcx.eval_to_allocation_raw(key) { + Err(ErrorHandled::Silent) => bug!("unexpected error for {:?}", key), + Err(ErrorHandled::TooGeneric) => {} + // deduplicate calls + other => return other, + } + } + } + + if cfg!(debug_assertions) { + // Make sure we format the instance even if we do not print it. + // This serves as a regression test against an ICE on printing. + // The next two lines concatenated contain some discussion: + // https://rust-lang.zulipchat.com/#narrow/stream/146212-t-compiler.2Fconst-eval/ + // subject/anon_const_instance_printing/near/135980032 + let instance = with_no_trimmed_paths(|| key.value.instance.to_string()); + trace!("const eval: {:?} ({})", key, instance); + } + + let cid = key.value; + let def = cid.instance.def.with_opt_param(); + + if let Some(def) = def.as_local() { + if tcx.has_typeck_results(def.did) { + if let Some(error_reported) = tcx.typeck_opt_const_arg(def).tainted_by_errors { + return Err(ErrorHandled::Reported(error_reported)); + } + } + if !tcx.is_mir_available(def.did) { + tcx.sess.delay_span_bug( + tcx.def_span(def.did), + &format!("no MIR body is available for {:?}", def.did), + ); + return Err(ErrorHandled::Reported(ErrorReported {})); + } + if let Some(error_reported) = tcx.mir_const_qualif_opt_const_arg(def).error_occured { + return Err(ErrorHandled::Reported(error_reported)); + } + } + + let is_static = tcx.is_static(def.did); + + let mut ecx = InterpCx::new( + tcx, + tcx.def_span(def.did), + key.param_env, + CompileTimeInterpreter::new(tcx.sess.const_eval_limit()), + MemoryExtra { can_access_statics: is_static }, + ); + + let res = ecx.load_mir(cid.instance.def, cid.promoted); + match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) { + Err(error) => { + if key.param_env.reveal() == Reveal::Selection { + match error.kind { + err_inval!(Layout(LayoutError::Unknown(_))) + | err_inval!(TooGeneric) + | err_inval!(AlreadyReported(_)) => {} + _ => return Err(ErrorHandled::Silent), + } + } + + let err = ConstEvalErr::new(&ecx, error, None); + // errors in statics are always emitted as fatal errors + if is_static { + // Ensure that if the above error was either `TooGeneric` or `Reported` + // an error must be reported. + let v = err.report_as_error( + ecx.tcx.at(ecx.cur_span()), + "could not evaluate static initializer", + ); + + // If this is `Reveal:All`, then we need to make sure an error is reported but if + // this is `Reveal::UserFacing`, then it's expected that we could get a + // `TooGeneric` error. When we fall back to `Reveal::All`, then it will either + // succeed or we'll report this error then. + if key.param_env.reveal() == Reveal::All { + tcx.sess.delay_span_bug( + err.span, + &format!("static eval failure did not emit an error: {:#?}", v), + ); + } + + Err(v) + } else if let Some(def) = def.as_local() { + // constant defined in this crate, we can figure out a lint level! + match tcx.def_kind(def.did.to_def_id()) { + // constants never produce a hard error at the definition site. Anything else is + // a backwards compatibility hazard (and will break old versions of winapi for + // sure) + // + // note that validation may still cause a hard error on this very same constant, + // because any code that existed before validation could not have failed + // validation thus preventing such a hard error from being a backwards + // compatibility hazard + DefKind::Const | DefKind::AssocConst => { + let hir_id = tcx.hir().local_def_id_to_hir_id(def.did); + Err(err.report_as_lint( + tcx.at(tcx.def_span(def.did)), + "any use of this value will cause an error", + hir_id, + Some(err.span), + )) + } + // promoting runtime code is only allowed to error if it references broken + // constants any other kind of error will be reported to the user as a + // deny-by-default lint + _ => { + if let Some(p) = cid.promoted { + let span = tcx.promoted_mir_opt_const_arg(def.to_global())[p].span; + if let err_inval!(ReferencedConstant) = err.error { + Err(err.report_as_error( + tcx.at(span), + "evaluation of constant expression failed", + )) + } else { + Err(err.report_as_lint( + tcx.at(span), + "reaching this expression at runtime will panic or abort", + tcx.hir().local_def_id_to_hir_id(def.did), + Some(err.span), + )) + } + // anything else (array lengths, enum initializers, constant patterns) are + // reported as hard errors + } else { + Err(err.report_as_error( + ecx.tcx.at(ecx.cur_span()), + "evaluation of constant value failed", + )) + } + } + } + } else { + // use of broken constant from other crate + Err(err.report_as_error(ecx.tcx.at(ecx.cur_span()), "could not evaluate constant")) + } + } + Ok(mplace) => { + // Since evaluation had no errors, valiate the resulting constant: + let validation = try { + let mut ref_tracking = RefTracking::new(mplace); + let mut inner = false; + while let Some((mplace, path)) = ref_tracking.todo.pop() { + let mode = match tcx.static_mutability(cid.instance.def_id()) { + Some(_) if cid.promoted.is_some() => { + // Promoteds in statics are allowed to point to statics. + CtfeValidationMode::Const { inner, allow_static_ptrs: true } + } + Some(_) => CtfeValidationMode::Regular, // a `static` + None => CtfeValidationMode::Const { inner, allow_static_ptrs: false }, + }; + ecx.const_validate_operand(mplace.into(), path, &mut ref_tracking, mode)?; + inner = true; + } + }; + if let Err(error) = validation { + // Validation failed, report an error + let err = ConstEvalErr::new(&ecx, error, None); + Err(err.struct_error( + ecx.tcx, + "it is undefined behavior to use this value", + |mut diag| { + diag.note(note_on_undefined_behavior_error()); + diag.emit(); + }, + )) + } else { + // Convert to raw constant + Ok(ConstAlloc { alloc_id: mplace.ptr.assert_ptr().alloc_id, ty: mplace.layout.ty }) + } + } + } +} diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs new file mode 100644 index 0000000000000..a2a58207f279f --- /dev/null +++ b/compiler/rustc_mir/src/monomorphize/collector.rs @@ -0,0 +1,1265 @@ +//! Mono Item Collection +//! ==================== +//! +//! This module is responsible for discovering all items that will contribute +//! to code generation of the crate. The important part here is that it not only +//! needs to find syntax-level items (functions, structs, etc) but also all +//! their monomorphized instantiations. Every non-generic, non-const function +//! maps to one LLVM artifact. Every generic function can produce +//! from zero to N artifacts, depending on the sets of type arguments it +//! is instantiated with. +//! This also applies to generic items from other crates: A generic definition +//! in crate X might produce monomorphizations that are compiled into crate Y. +//! We also have to collect these here. +//! +//! The following kinds of "mono items" are handled here: +//! +//! - Functions +//! - Methods +//! - Closures +//! - Statics +//! - Drop glue +//! +//! The following things also result in LLVM artifacts, but are not collected +//! here, since we instantiate them locally on demand when needed in a given +//! codegen unit: +//! +//! - Constants +//! - Vtables +//! - Object Shims +//! +//! +//! General Algorithm +//! ----------------- +//! Let's define some terms first: +//! +//! - A "mono item" is something that results in a function or global in +//! the LLVM IR of a codegen unit. Mono items do not stand on their +//! own, they can reference other mono items. For example, if function +//! `foo()` calls function `bar()` then the mono item for `foo()` +//! references the mono item for function `bar()`. In general, the +//! definition for mono item A referencing a mono item B is that +//! the LLVM artifact produced for A references the LLVM artifact produced +//! for B. +//! +//! - Mono items and the references between them form a directed graph, +//! where the mono items are the nodes and references form the edges. +//! Let's call this graph the "mono item graph". +//! +//! - The mono item graph for a program contains all mono items +//! that are needed in order to produce the complete LLVM IR of the program. +//! +//! The purpose of the algorithm implemented in this module is to build the +//! mono item graph for the current crate. It runs in two phases: +//! +//! 1. Discover the roots of the graph by traversing the HIR of the crate. +//! 2. Starting from the roots, find neighboring nodes by inspecting the MIR +//! representation of the item corresponding to a given node, until no more +//! new nodes are found. +//! +//! ### Discovering roots +//! +//! The roots of the mono item graph correspond to the non-generic +//! syntactic items in the source code. We find them by walking the HIR of the +//! crate, and whenever we hit upon a function, method, or static item, we +//! create a mono item consisting of the items DefId and, since we only +//! consider non-generic items, an empty type-substitution set. +//! +//! ### Finding neighbor nodes +//! Given a mono item node, we can discover neighbors by inspecting its +//! MIR. We walk the MIR and any time we hit upon something that signifies a +//! reference to another mono item, we have found a neighbor. Since the +//! mono item we are currently at is always monomorphic, we also know the +//! concrete type arguments of its neighbors, and so all neighbors again will be +//! monomorphic. The specific forms a reference to a neighboring node can take +//! in MIR are quite diverse. Here is an overview: +//! +//! #### Calling Functions/Methods +//! The most obvious form of one mono item referencing another is a +//! function or method call (represented by a CALL terminator in MIR). But +//! calls are not the only thing that might introduce a reference between two +//! function mono items, and as we will see below, they are just a +//! specialization of the form described next, and consequently will not get any +//! special treatment in the algorithm. +//! +//! #### Taking a reference to a function or method +//! A function does not need to actually be called in order to be a neighbor of +//! another function. It suffices to just take a reference in order to introduce +//! an edge. Consider the following example: +//! +//! ```rust +//! fn print_val(x: T) { +//! println!("{}", x); +//! } +//! +//! fn call_fn(f: &Fn(i32), x: i32) { +//! f(x); +//! } +//! +//! fn main() { +//! let print_i32 = print_val::; +//! call_fn(&print_i32, 0); +//! } +//! ``` +//! The MIR of none of these functions will contain an explicit call to +//! `print_val::`. Nonetheless, in order to mono this program, we need +//! an instance of this function. Thus, whenever we encounter a function or +//! method in operand position, we treat it as a neighbor of the current +//! mono item. Calls are just a special case of that. +//! +//! #### Closures +//! In a way, closures are a simple case. Since every closure object needs to be +//! constructed somewhere, we can reliably discover them by observing +//! `RValue::Aggregate` expressions with `AggregateKind::Closure`. This is also +//! true for closures inlined from other crates. +//! +//! #### Drop glue +//! Drop glue mono items are introduced by MIR drop-statements. The +//! generated mono item will again have drop-glue item neighbors if the +//! type to be dropped contains nested values that also need to be dropped. It +//! might also have a function item neighbor for the explicit `Drop::drop` +//! implementation of its type. +//! +//! #### Unsizing Casts +//! A subtle way of introducing neighbor edges is by casting to a trait object. +//! Since the resulting fat-pointer contains a reference to a vtable, we need to +//! instantiate all object-save methods of the trait, as we need to store +//! pointers to these functions even if they never get called anywhere. This can +//! be seen as a special case of taking a function reference. +//! +//! #### Boxes +//! Since `Box` expression have special compiler support, no explicit calls to +//! `exchange_malloc()` and `box_free()` may show up in MIR, even if the +//! compiler will generate them. We have to observe `Rvalue::Box` expressions +//! and Box-typed drop-statements for that purpose. +//! +//! +//! Interaction with Cross-Crate Inlining +//! ------------------------------------- +//! The binary of a crate will not only contain machine code for the items +//! defined in the source code of that crate. It will also contain monomorphic +//! instantiations of any extern generic functions and of functions marked with +//! `#[inline]`. +//! The collection algorithm handles this more or less mono. If it is +//! about to create a mono item for something with an external `DefId`, +//! it will take a look if the MIR for that item is available, and if so just +//! proceed normally. If the MIR is not available, it assumes that the item is +//! just linked to and no node is created; which is exactly what we want, since +//! no machine code should be generated in the current crate for such an item. +//! +//! Eager and Lazy Collection Mode +//! ------------------------------ +//! Mono item collection can be performed in one of two modes: +//! +//! - Lazy mode means that items will only be instantiated when actually +//! referenced. The goal is to produce the least amount of machine code +//! possible. +//! +//! - Eager mode is meant to be used in conjunction with incremental compilation +//! where a stable set of mono items is more important than a minimal +//! one. Thus, eager mode will instantiate drop-glue for every drop-able type +//! in the crate, even if no drop call for that type exists (yet). It will +//! also instantiate default implementations of trait methods, something that +//! otherwise is only done on demand. +//! +//! +//! Open Issues +//! ----------- +//! Some things are not yet fully implemented in the current version of this +//! module. +//! +//! ### Const Fns +//! Ideally, no mono item should be generated for const fns unless there +//! is a call to them that cannot be evaluated at compile time. At the moment +//! this is not implemented however: a mono item will be produced +//! regardless of whether it is actually needed or not. + +use crate::monomorphize; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::sync::{par_iter, MTLock, MTRef, ParallelIterator}; +use rustc_errors::{ErrorReported, FatalError}; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; +use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_hir::lang_items::LangItem; +use rustc_index::bit_set::GrowableBitSet; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::mir::interpret::{AllocId, ConstValue}; +use rustc_middle::mir::interpret::{ErrorHandled, GlobalAlloc, Scalar}; +use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; +use rustc_middle::mir::visit::Visitor as MirVisitor; +use rustc_middle::mir::{self, Local, Location}; +use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCast}; +use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; +use rustc_middle::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable}; +use rustc_session::config::EntryFnType; +use rustc_span::source_map::{dummy_spanned, respan, Span, Spanned, DUMMY_SP}; +use smallvec::SmallVec; +use std::iter; +use std::ops::Range; +use std::path::PathBuf; + +#[derive(PartialEq)] +pub enum MonoItemCollectionMode { + Eager, + Lazy, +} + +/// Maps every mono item to all mono items it references in its +/// body. +pub struct InliningMap<'tcx> { + // Maps a source mono item to the range of mono items + // accessed by it. + // The range selects elements within the `targets` vecs. + index: FxHashMap, Range>, + targets: Vec>, + + // Contains one bit per mono item in the `targets` field. That bit + // is true if that mono item needs to be inlined into every CGU. + inlines: GrowableBitSet, +} + +impl<'tcx> InliningMap<'tcx> { + fn new() -> InliningMap<'tcx> { + InliningMap { + index: FxHashMap::default(), + targets: Vec::new(), + inlines: GrowableBitSet::with_capacity(1024), + } + } + + fn record_accesses(&mut self, source: MonoItem<'tcx>, new_targets: &[(MonoItem<'tcx>, bool)]) { + let start_index = self.targets.len(); + let new_items_count = new_targets.len(); + let new_items_count_total = new_items_count + self.targets.len(); + + self.targets.reserve(new_items_count); + self.inlines.ensure(new_items_count_total); + + for (i, (target, inline)) in new_targets.iter().enumerate() { + self.targets.push(*target); + if *inline { + self.inlines.insert(i + start_index); + } + } + + let end_index = self.targets.len(); + assert!(self.index.insert(source, start_index..end_index).is_none()); + } + + // Internally iterate over all items referenced by `source` which will be + // made available for inlining. + pub fn with_inlining_candidates(&self, source: MonoItem<'tcx>, mut f: F) + where + F: FnMut(MonoItem<'tcx>), + { + if let Some(range) = self.index.get(&source) { + for (i, candidate) in self.targets[range.clone()].iter().enumerate() { + if self.inlines.contains(range.start + i) { + f(*candidate); + } + } + } + } + + // Internally iterate over all items and the things each accesses. + pub fn iter_accesses(&self, mut f: F) + where + F: FnMut(MonoItem<'tcx>, &[MonoItem<'tcx>]), + { + for (&accessor, range) in &self.index { + f(accessor, &self.targets[range.clone()]) + } + } +} + +pub fn collect_crate_mono_items( + tcx: TyCtxt<'_>, + mode: MonoItemCollectionMode, +) -> (FxHashSet>, InliningMap<'_>) { + let _prof_timer = tcx.prof.generic_activity("monomorphization_collector"); + + let roots = + tcx.sess.time("monomorphization_collector_root_collections", || collect_roots(tcx, mode)); + + debug!("building mono item graph, beginning at roots"); + + let mut visited = MTLock::new(FxHashSet::default()); + let mut inlining_map = MTLock::new(InliningMap::new()); + + { + let visited: MTRef<'_, _> = &mut visited; + let inlining_map: MTRef<'_, _> = &mut inlining_map; + + tcx.sess.time("monomorphization_collector_graph_walk", || { + par_iter(roots).for_each(|root| { + let mut recursion_depths = DefIdMap::default(); + collect_items_rec( + tcx, + dummy_spanned(root), + visited, + &mut recursion_depths, + inlining_map, + ); + }); + }); + } + + (visited.into_inner(), inlining_map.into_inner()) +} + +// Find all non-generic items by walking the HIR. These items serve as roots to +// start monomorphizing from. +fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionMode) -> Vec> { + debug!("collecting roots"); + let mut roots = Vec::new(); + + { + let entry_fn = tcx.entry_fn(LOCAL_CRATE); + + debug!("collect_roots: entry_fn = {:?}", entry_fn); + + let mut visitor = RootCollector { tcx, mode, entry_fn, output: &mut roots }; + + tcx.hir().krate().visit_all_item_likes(&mut visitor); + + visitor.push_extra_entry_roots(); + } + + // We can only codegen items that are instantiable - items all of + // whose predicates hold. Luckily, items that aren't instantiable + // can't actually be used, so we can just skip codegenning them. + roots + .into_iter() + .filter_map(|root| root.node.is_instantiable(tcx).then_some(root.node)) + .collect() +} + +// Collect all monomorphized items reachable from `starting_point` +fn collect_items_rec<'tcx>( + tcx: TyCtxt<'tcx>, + starting_point: Spanned>, + visited: MTRef<'_, MTLock>>>, + recursion_depths: &mut DefIdMap, + inlining_map: MTRef<'_, MTLock>>, +) { + if !visited.lock_mut().insert(starting_point.node) { + // We've been here already, no need to search again. + return; + } + debug!("BEGIN collect_items_rec({})", starting_point.node); + + let mut neighbors = Vec::new(); + let recursion_depth_reset; + + match starting_point.node { + MonoItem::Static(def_id) => { + let instance = Instance::mono(tcx, def_id); + + // Sanity check whether this ended up being collected accidentally + debug_assert!(should_codegen_locally(tcx, &instance)); + + let ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); + visit_drop_use(tcx, ty, true, starting_point.span, &mut neighbors); + + recursion_depth_reset = None; + + if let Ok(alloc) = tcx.eval_static_initializer(def_id) { + for &((), id) in alloc.relocations().values() { + collect_miri(tcx, id, &mut neighbors); + } + } + } + MonoItem::Fn(instance) => { + // Sanity check whether this ended up being collected accidentally + debug_assert!(should_codegen_locally(tcx, &instance)); + + // Keep track of the monomorphization recursion depth + recursion_depth_reset = + Some(check_recursion_limit(tcx, instance, starting_point.span, recursion_depths)); + check_type_length_limit(tcx, instance); + + rustc_data_structures::stack::ensure_sufficient_stack(|| { + collect_neighbours(tcx, instance, &mut neighbors); + }); + } + MonoItem::GlobalAsm(..) => { + recursion_depth_reset = None; + } + } + + record_accesses(tcx, starting_point.node, neighbors.iter().map(|i| &i.node), inlining_map); + + for neighbour in neighbors { + collect_items_rec(tcx, neighbour, visited, recursion_depths, inlining_map); + } + + if let Some((def_id, depth)) = recursion_depth_reset { + recursion_depths.insert(def_id, depth); + } + + debug!("END collect_items_rec({})", starting_point.node); +} + +fn record_accesses<'a, 'tcx: 'a>( + tcx: TyCtxt<'tcx>, + caller: MonoItem<'tcx>, + callees: impl Iterator>, + inlining_map: MTRef<'_, MTLock>>, +) { + let is_inlining_candidate = |mono_item: &MonoItem<'tcx>| { + mono_item.instantiation_mode(tcx) == InstantiationMode::LocalCopy + }; + + // We collect this into a `SmallVec` to avoid calling `is_inlining_candidate` in the lock. + // FIXME: Call `is_inlining_candidate` when pushing to `neighbors` in `collect_items_rec` + // instead to avoid creating this `SmallVec`. + let accesses: SmallVec<[_; 128]> = + callees.map(|mono_item| (*mono_item, is_inlining_candidate(mono_item))).collect(); + + inlining_map.lock_mut().record_accesses(caller, &accesses); +} + +/// Format instance name that is already known to be too long for rustc. +/// Show only the first and last 32 characters to avoid blasting +/// the user's terminal with thousands of lines of type-name. +/// +/// If the type name is longer than before+after, it will be written to a file. +fn shrunk_instance_name( + tcx: TyCtxt<'tcx>, + instance: &Instance<'tcx>, + before: usize, + after: usize, +) -> (String, Option) { + let s = instance.to_string(); + + // Only use the shrunk version if it's really shorter. + // This also avoids the case where before and after slices overlap. + if s.chars().nth(before + after + 1).is_some() { + // An iterator of all byte positions including the end of the string. + let positions = || s.char_indices().map(|(i, _)| i).chain(iter::once(s.len())); + + let shrunk = format!( + "{before}...{after}", + before = &s[..positions().nth(before).unwrap_or(s.len())], + after = &s[positions().rev().nth(after).unwrap_or(0)..], + ); + + let path = tcx.output_filenames(LOCAL_CRATE).temp_path_ext("long-type.txt", None); + let written_to_path = std::fs::write(&path, s).ok().map(|_| path); + + (shrunk, written_to_path) + } else { + (s, None) + } +} + +fn check_recursion_limit<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + span: Span, + recursion_depths: &mut DefIdMap, +) -> (DefId, usize) { + let def_id = instance.def_id(); + let recursion_depth = recursion_depths.get(&def_id).cloned().unwrap_or(0); + debug!(" => recursion depth={}", recursion_depth); + + let adjusted_recursion_depth = if Some(def_id) == tcx.lang_items().drop_in_place_fn() { + // HACK: drop_in_place creates tight monomorphization loops. Give + // it more margin. + recursion_depth / 4 + } else { + recursion_depth + }; + + // Code that needs to instantiate the same function recursively + // more than the recursion limit is assumed to be causing an + // infinite expansion. + if !tcx.sess.recursion_limit().value_within_limit(adjusted_recursion_depth) { + let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance, 32, 32); + let error = format!("reached the recursion limit while instantiating `{}`", shrunk); + let mut err = tcx.sess.struct_span_fatal(span, &error); + err.span_note( + tcx.def_span(def_id), + &format!("`{}` defined here", tcx.def_path_str(def_id)), + ); + if let Some(path) = written_to_path { + err.note(&format!("the full type name has been written to '{}'", path.display())); + } + err.emit(); + FatalError.raise(); + } + + recursion_depths.insert(def_id, recursion_depth + 1); + + (def_id, recursion_depth) +} + +fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { + let type_length = instance + .substs + .iter() + .flat_map(|arg| arg.walk()) + .filter(|arg| match arg.unpack() { + GenericArgKind::Type(_) | GenericArgKind::Const(_) => true, + GenericArgKind::Lifetime(_) => false, + }) + .count(); + debug!(" => type length={}", type_length); + + // Rust code can easily create exponentially-long types using only a + // polynomial recursion depth. Even with the default recursion + // depth, you can easily get cases that take >2^60 steps to run, + // which means that rustc basically hangs. + // + // Bail out in these cases to avoid that bad user experience. + if !tcx.sess.type_length_limit().value_within_limit(type_length) { + let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance, 32, 32); + let msg = format!("reached the type-length limit while instantiating `{}`", shrunk); + let mut diag = tcx.sess.struct_span_fatal(tcx.def_span(instance.def_id()), &msg); + if let Some(path) = written_to_path { + diag.note(&format!("the full type name has been written to '{}'", path.display())); + } + diag.help(&format!( + "consider adding a `#![type_length_limit=\"{}\"]` attribute to your crate", + type_length + )); + diag.emit(); + tcx.sess.abort_if_errors(); + } +} + +struct MirNeighborCollector<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + body: &'a mir::Body<'tcx>, + output: &'a mut Vec>>, + instance: Instance<'tcx>, +} + +impl<'a, 'tcx> MirNeighborCollector<'a, 'tcx> { + pub fn monomorphize(&self, value: T) -> T + where + T: TypeFoldable<'tcx>, + { + debug!("monomorphize: self.instance={:?}", self.instance); + self.instance.subst_mir_and_normalize_erasing_regions( + self.tcx, + ty::ParamEnv::reveal_all(), + value, + ) + } +} + +impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { + fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { + debug!("visiting rvalue {:?}", *rvalue); + + let span = self.body.source_info(location).span; + + match *rvalue { + // When doing an cast from a regular pointer to a fat pointer, we + // have to instantiate all methods of the trait being cast to, so we + // can build the appropriate vtable. + mir::Rvalue::Cast( + mir::CastKind::Pointer(PointerCast::Unsize), + ref operand, + target_ty, + ) => { + let target_ty = self.monomorphize(target_ty); + let source_ty = operand.ty(self.body, self.tcx); + let source_ty = self.monomorphize(source_ty); + let (source_ty, target_ty) = + find_vtable_types_for_unsizing(self.tcx, source_ty, target_ty); + // This could also be a different Unsize instruction, like + // from a fixed sized array to a slice. But we are only + // interested in things that produce a vtable. + if target_ty.is_trait() && !source_ty.is_trait() { + create_mono_items_for_vtable_methods( + self.tcx, + target_ty, + source_ty, + span, + self.output, + ); + } + } + mir::Rvalue::Cast( + mir::CastKind::Pointer(PointerCast::ReifyFnPointer), + ref operand, + _, + ) => { + let fn_ty = operand.ty(self.body, self.tcx); + let fn_ty = self.monomorphize(fn_ty); + visit_fn_use(self.tcx, fn_ty, false, span, &mut self.output); + } + mir::Rvalue::Cast( + mir::CastKind::Pointer(PointerCast::ClosureFnPointer(_)), + ref operand, + _, + ) => { + let source_ty = operand.ty(self.body, self.tcx); + let source_ty = self.monomorphize(source_ty); + match *source_ty.kind() { + ty::Closure(def_id, substs) => { + let instance = Instance::resolve_closure( + self.tcx, + def_id, + substs, + ty::ClosureKind::FnOnce, + ); + if should_codegen_locally(self.tcx, &instance) { + self.output.push(create_fn_mono_item(self.tcx, instance, span)); + } + } + _ => bug!(), + } + } + mir::Rvalue::NullaryOp(mir::NullOp::Box, _) => { + let tcx = self.tcx; + let exchange_malloc_fn_def_id = + tcx.require_lang_item(LangItem::ExchangeMalloc, None); + let instance = Instance::mono(tcx, exchange_malloc_fn_def_id); + if should_codegen_locally(tcx, &instance) { + self.output.push(create_fn_mono_item(self.tcx, instance, span)); + } + } + mir::Rvalue::ThreadLocalRef(def_id) => { + assert!(self.tcx.is_thread_local_static(def_id)); + let instance = Instance::mono(self.tcx, def_id); + if should_codegen_locally(self.tcx, &instance) { + trace!("collecting thread-local static {:?}", def_id); + self.output.push(respan(span, MonoItem::Static(def_id))); + } + } + _ => { /* not interesting */ } + } + + self.super_rvalue(rvalue, location); + } + + fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, location: Location) { + debug!("visiting const {:?} @ {:?}", *constant, location); + + let substituted_constant = self.monomorphize(*constant); + let param_env = ty::ParamEnv::reveal_all(); + + match substituted_constant.val { + ty::ConstKind::Value(val) => collect_const_value(self.tcx, val, self.output), + ty::ConstKind::Unevaluated(def, substs, promoted) => { + match self.tcx.const_eval_resolve(param_env, def, substs, promoted, None) { + Ok(val) => collect_const_value(self.tcx, val, self.output), + Err(ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted) => {} + Err(ErrorHandled::TooGeneric) => span_bug!( + self.body.source_info(location).span, + "collection encountered polymorphic constant: {}", + substituted_constant + ), + Err(ErrorHandled::Silent) => span_bug!( + self.body.source_info(location).span, + "silent error emitted during collection", + ), + } + } + _ => {} + } + + self.super_const(constant); + } + + fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { + debug!("visiting terminator {:?} @ {:?}", terminator, location); + let source = self.body.source_info(location).span; + + let tcx = self.tcx; + match terminator.kind { + mir::TerminatorKind::Call { ref func, .. } => { + let callee_ty = func.ty(self.body, tcx); + let callee_ty = self.monomorphize(callee_ty); + visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output); + } + mir::TerminatorKind::Drop { ref place, .. } + | mir::TerminatorKind::DropAndReplace { ref place, .. } => { + let ty = place.ty(self.body, self.tcx).ty; + let ty = self.monomorphize(ty); + visit_drop_use(self.tcx, ty, true, source, self.output); + } + mir::TerminatorKind::InlineAsm { ref operands, .. } => { + for op in operands { + match *op { + mir::InlineAsmOperand::SymFn { ref value } => { + let fn_ty = self.monomorphize(value.literal.ty); + visit_fn_use(self.tcx, fn_ty, false, source, &mut self.output); + } + mir::InlineAsmOperand::SymStatic { def_id } => { + let instance = Instance::mono(self.tcx, def_id); + if should_codegen_locally(self.tcx, &instance) { + trace!("collecting asm sym static {:?}", def_id); + self.output.push(respan(source, MonoItem::Static(def_id))); + } + } + _ => {} + } + } + } + mir::TerminatorKind::Goto { .. } + | mir::TerminatorKind::SwitchInt { .. } + | mir::TerminatorKind::Resume + | mir::TerminatorKind::Abort + | mir::TerminatorKind::Return + | mir::TerminatorKind::Unreachable + | mir::TerminatorKind::Assert { .. } => {} + mir::TerminatorKind::GeneratorDrop + | mir::TerminatorKind::Yield { .. } + | mir::TerminatorKind::FalseEdge { .. } + | mir::TerminatorKind::FalseUnwind { .. } => bug!(), + } + + self.super_terminator(terminator, location); + } + + fn visit_local( + &mut self, + _place_local: &Local, + _context: mir::visit::PlaceContext, + _location: Location, + ) { + } +} + +fn visit_drop_use<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + is_direct_call: bool, + source: Span, + output: &mut Vec>>, +) { + let instance = Instance::resolve_drop_in_place(tcx, ty); + visit_instance_use(tcx, instance, is_direct_call, source, output); +} + +fn visit_fn_use<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + is_direct_call: bool, + source: Span, + output: &mut Vec>>, +) { + if let ty::FnDef(def_id, substs) = *ty.kind() { + let instance = if is_direct_call { + ty::Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, substs).unwrap().unwrap() + } else { + ty::Instance::resolve_for_fn_ptr(tcx, ty::ParamEnv::reveal_all(), def_id, substs) + .unwrap() + }; + visit_instance_use(tcx, instance, is_direct_call, source, output); + } +} + +fn visit_instance_use<'tcx>( + tcx: TyCtxt<'tcx>, + instance: ty::Instance<'tcx>, + is_direct_call: bool, + source: Span, + output: &mut Vec>>, +) { + debug!("visit_item_use({:?}, is_direct_call={:?})", instance, is_direct_call); + if !should_codegen_locally(tcx, &instance) { + return; + } + + match instance.def { + ty::InstanceDef::Virtual(..) | ty::InstanceDef::Intrinsic(_) => { + if !is_direct_call { + bug!("{:?} being reified", instance); + } + } + ty::InstanceDef::DropGlue(_, None) => { + // Don't need to emit noop drop glue if we are calling directly. + if !is_direct_call { + output.push(create_fn_mono_item(tcx, instance, source)); + } + } + ty::InstanceDef::DropGlue(_, Some(_)) + | ty::InstanceDef::VtableShim(..) + | ty::InstanceDef::ReifyShim(..) + | ty::InstanceDef::ClosureOnceShim { .. } + | ty::InstanceDef::Item(..) + | ty::InstanceDef::FnPtrShim(..) + | ty::InstanceDef::CloneShim(..) => { + output.push(create_fn_mono_item(tcx, instance, source)); + } + } +} + +// Returns `true` if we should codegen an instance in the local crate. +// Returns `false` if we can just link to the upstream crate and therefore don't +// need a mono item. +fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> bool { + let def_id = match instance.def { + ty::InstanceDef::Item(def) => def.did, + ty::InstanceDef::DropGlue(def_id, Some(_)) => def_id, + ty::InstanceDef::VtableShim(..) + | ty::InstanceDef::ReifyShim(..) + | ty::InstanceDef::ClosureOnceShim { .. } + | ty::InstanceDef::Virtual(..) + | ty::InstanceDef::FnPtrShim(..) + | ty::InstanceDef::DropGlue(..) + | ty::InstanceDef::Intrinsic(_) + | ty::InstanceDef::CloneShim(..) => return true, + }; + + if tcx.is_foreign_item(def_id) { + // Foreign items are always linked against, there's no way of instantiating them. + return false; + } + + if def_id.is_local() { + // Local items cannot be referred to locally without monomorphizing them locally. + return true; + } + + if tcx.is_reachable_non_generic(def_id) + || instance.polymorphize(tcx).upstream_monomorphization(tcx).is_some() + { + // We can link to the item in question, no instance needed in this crate. + return false; + } + + if !tcx.is_mir_available(def_id) { + bug!("no MIR available for {:?}", def_id); + } + + true +} + +/// For a given pair of source and target type that occur in an unsizing coercion, +/// this function finds the pair of types that determines the vtable linking +/// them. +/// +/// For example, the source type might be `&SomeStruct` and the target type\ +/// might be `&SomeTrait` in a cast like: +/// +/// let src: &SomeStruct = ...; +/// let target = src as &SomeTrait; +/// +/// Then the output of this function would be (SomeStruct, SomeTrait) since for +/// constructing the `target` fat-pointer we need the vtable for that pair. +/// +/// Things can get more complicated though because there's also the case where +/// the unsized type occurs as a field: +/// +/// ```rust +/// struct ComplexStruct { +/// a: u32, +/// b: f64, +/// c: T +/// } +/// ``` +/// +/// In this case, if `T` is sized, `&ComplexStruct` is a thin pointer. If `T` +/// is unsized, `&SomeStruct` is a fat pointer, and the vtable it points to is +/// for the pair of `T` (which is a trait) and the concrete type that `T` was +/// originally coerced from: +/// +/// let src: &ComplexStruct = ...; +/// let target = src as &ComplexStruct; +/// +/// Again, we want this `find_vtable_types_for_unsizing()` to provide the pair +/// `(SomeStruct, SomeTrait)`. +/// +/// Finally, there is also the case of custom unsizing coercions, e.g., for +/// smart pointers such as `Rc` and `Arc`. +fn find_vtable_types_for_unsizing<'tcx>( + tcx: TyCtxt<'tcx>, + source_ty: Ty<'tcx>, + target_ty: Ty<'tcx>, +) -> (Ty<'tcx>, Ty<'tcx>) { + let ptr_vtable = |inner_source: Ty<'tcx>, inner_target: Ty<'tcx>| { + let param_env = ty::ParamEnv::reveal_all(); + let type_has_metadata = |ty: Ty<'tcx>| -> bool { + if ty.is_sized(tcx.at(DUMMY_SP), param_env) { + return false; + } + let tail = tcx.struct_tail_erasing_lifetimes(ty, param_env); + match tail.kind() { + ty::Foreign(..) => false, + ty::Str | ty::Slice(..) | ty::Dynamic(..) => true, + _ => bug!("unexpected unsized tail: {:?}", tail), + } + }; + if type_has_metadata(inner_source) { + (inner_source, inner_target) + } else { + tcx.struct_lockstep_tails_erasing_lifetimes(inner_source, inner_target, param_env) + } + }; + + match (&source_ty.kind(), &target_ty.kind()) { + (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) + | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { + ptr_vtable(a, b) + } + (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => { + ptr_vtable(source_ty.boxed_ty(), target_ty.boxed_ty()) + } + + (&ty::Adt(source_adt_def, source_substs), &ty::Adt(target_adt_def, target_substs)) => { + assert_eq!(source_adt_def, target_adt_def); + + let CustomCoerceUnsized::Struct(coerce_index) = + monomorphize::custom_coerce_unsize_info(tcx, source_ty, target_ty); + + let source_fields = &source_adt_def.non_enum_variant().fields; + let target_fields = &target_adt_def.non_enum_variant().fields; + + assert!( + coerce_index < source_fields.len() && source_fields.len() == target_fields.len() + ); + + find_vtable_types_for_unsizing( + tcx, + source_fields[coerce_index].ty(tcx, source_substs), + target_fields[coerce_index].ty(tcx, target_substs), + ) + } + _ => bug!( + "find_vtable_types_for_unsizing: invalid coercion {:?} -> {:?}", + source_ty, + target_ty + ), + } +} + +fn create_fn_mono_item<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + source: Span, +) -> Spanned> { + debug!("create_fn_mono_item(instance={})", instance); + respan(source, MonoItem::Fn(instance.polymorphize(tcx))) +} + +/// Creates a `MonoItem` for each method that is referenced by the vtable for +/// the given trait/impl pair. +fn create_mono_items_for_vtable_methods<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ty: Ty<'tcx>, + impl_ty: Ty<'tcx>, + source: Span, + output: &mut Vec>>, +) { + assert!(!trait_ty.has_escaping_bound_vars() && !impl_ty.has_escaping_bound_vars()); + + if let ty::Dynamic(ref trait_ty, ..) = trait_ty.kind() { + if let Some(principal) = trait_ty.principal() { + let poly_trait_ref = principal.with_self_ty(tcx, impl_ty); + assert!(!poly_trait_ref.has_escaping_bound_vars()); + + // Walk all methods of the trait, including those of its supertraits + let methods = tcx.vtable_methods(poly_trait_ref); + let methods = methods + .iter() + .cloned() + .filter_map(|method| method) + .map(|(def_id, substs)| { + ty::Instance::resolve_for_vtable( + tcx, + ty::ParamEnv::reveal_all(), + def_id, + substs, + ) + .unwrap() + }) + .filter(|&instance| should_codegen_locally(tcx, &instance)) + .map(|item| create_fn_mono_item(tcx, item, source)); + output.extend(methods); + } + + // Also add the destructor. + visit_drop_use(tcx, impl_ty, false, source, output); + } +} + +//=----------------------------------------------------------------------------- +// Root Collection +//=----------------------------------------------------------------------------- + +struct RootCollector<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + mode: MonoItemCollectionMode, + output: &'a mut Vec>>, + entry_fn: Option<(LocalDefId, EntryFnType)>, +} + +impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> { + fn visit_item(&mut self, item: &'v hir::Item<'v>) { + match item.kind { + hir::ItemKind::ExternCrate(..) + | hir::ItemKind::Use(..) + | hir::ItemKind::ForeignMod { .. } + | hir::ItemKind::TyAlias(..) + | hir::ItemKind::Trait(..) + | hir::ItemKind::TraitAlias(..) + | hir::ItemKind::OpaqueTy(..) + | hir::ItemKind::Mod(..) => { + // Nothing to do, just keep recursing. + } + + hir::ItemKind::Impl { .. } => { + if self.mode == MonoItemCollectionMode::Eager { + create_mono_items_for_default_impls(self.tcx, item, self.output); + } + } + + hir::ItemKind::Enum(_, ref generics) + | hir::ItemKind::Struct(_, ref generics) + | hir::ItemKind::Union(_, ref generics) => { + if generics.params.is_empty() { + if self.mode == MonoItemCollectionMode::Eager { + let def_id = self.tcx.hir().local_def_id(item.hir_id); + debug!( + "RootCollector: ADT drop-glue for {}", + self.tcx.def_path_str(def_id.to_def_id()) + ); + + let ty = Instance::new(def_id.to_def_id(), InternalSubsts::empty()) + .ty(self.tcx, ty::ParamEnv::reveal_all()); + visit_drop_use(self.tcx, ty, true, DUMMY_SP, self.output); + } + } + } + hir::ItemKind::GlobalAsm(..) => { + debug!( + "RootCollector: ItemKind::GlobalAsm({})", + self.tcx.def_path_str(self.tcx.hir().local_def_id(item.hir_id).to_def_id()) + ); + self.output.push(dummy_spanned(MonoItem::GlobalAsm(item.hir_id))); + } + hir::ItemKind::Static(..) => { + let def_id = self.tcx.hir().local_def_id(item.hir_id).to_def_id(); + debug!("RootCollector: ItemKind::Static({})", self.tcx.def_path_str(def_id)); + self.output.push(dummy_spanned(MonoItem::Static(def_id))); + } + hir::ItemKind::Const(..) => { + // const items only generate mono items if they are + // actually used somewhere. Just declaring them is insufficient. + + // but even just declaring them must collect the items they refer to + let def_id = self.tcx.hir().local_def_id(item.hir_id); + + if let Ok(val) = self.tcx.const_eval_poly(def_id.to_def_id()) { + collect_const_value(self.tcx, val, &mut self.output); + } + } + hir::ItemKind::Fn(..) => { + let def_id = self.tcx.hir().local_def_id(item.hir_id); + self.push_if_root(def_id); + } + } + } + + fn visit_trait_item(&mut self, _: &'v hir::TraitItem<'v>) { + // Even if there's a default body with no explicit generics, + // it's still generic over some `Self: Trait`, so not a root. + } + + fn visit_impl_item(&mut self, ii: &'v hir::ImplItem<'v>) { + if let hir::ImplItemKind::Fn(hir::FnSig { .. }, _) = ii.kind { + let def_id = self.tcx.hir().local_def_id(ii.hir_id); + self.push_if_root(def_id); + } + } + + fn visit_foreign_item(&mut self, _foreign_item: &'v hir::ForeignItem<'v>) {} +} + +impl RootCollector<'_, 'v> { + fn is_root(&self, def_id: LocalDefId) -> bool { + !item_requires_monomorphization(self.tcx, def_id) + && match self.mode { + MonoItemCollectionMode::Eager => true, + MonoItemCollectionMode::Lazy => { + self.entry_fn.map(|(id, _)| id) == Some(def_id) + || self.tcx.is_reachable_non_generic(def_id) + || self + .tcx + .codegen_fn_attrs(def_id) + .flags + .contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) + } + } + } + + /// If `def_id` represents a root, pushes it onto the list of + /// outputs. (Note that all roots must be monomorphic.) + fn push_if_root(&mut self, def_id: LocalDefId) { + if self.is_root(def_id) { + debug!("RootCollector::push_if_root: found root def_id={:?}", def_id); + + let instance = Instance::mono(self.tcx, def_id.to_def_id()); + self.output.push(create_fn_mono_item(self.tcx, instance, DUMMY_SP)); + } + } + + /// As a special case, when/if we encounter the + /// `main()` function, we also have to generate a + /// monomorphized copy of the start lang item based on + /// the return type of `main`. This is not needed when + /// the user writes their own `start` manually. + fn push_extra_entry_roots(&mut self) { + let main_def_id = match self.entry_fn { + Some((def_id, EntryFnType::Main)) => def_id, + _ => return, + }; + + let start_def_id = match self.tcx.lang_items().require(LangItem::Start) { + Ok(s) => s, + Err(err) => self.tcx.sess.fatal(&err), + }; + let main_ret_ty = self.tcx.fn_sig(main_def_id).output(); + + // Given that `main()` has no arguments, + // then its return type cannot have + // late-bound regions, since late-bound + // regions must appear in the argument + // listing. + let main_ret_ty = self.tcx.erase_regions(main_ret_ty.no_bound_vars().unwrap()); + + let start_instance = Instance::resolve( + self.tcx, + ty::ParamEnv::reveal_all(), + start_def_id, + self.tcx.intern_substs(&[main_ret_ty.into()]), + ) + .unwrap() + .unwrap(); + + self.output.push(create_fn_mono_item(self.tcx, start_instance, DUMMY_SP)); + } +} + +fn item_requires_monomorphization(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { + let generics = tcx.generics_of(def_id); + generics.requires_monomorphization(tcx) +} + +fn create_mono_items_for_default_impls<'tcx>( + tcx: TyCtxt<'tcx>, + item: &'tcx hir::Item<'tcx>, + output: &mut Vec>>, +) { + match item.kind { + hir::ItemKind::Impl(ref impl_) => { + for param in impl_.generics.params { + match param.kind { + hir::GenericParamKind::Lifetime { .. } => {} + hir::GenericParamKind::Type { .. } | hir::GenericParamKind::Const { .. } => { + return; + } + } + } + + let impl_def_id = tcx.hir().local_def_id(item.hir_id); + + debug!( + "create_mono_items_for_default_impls(item={})", + tcx.def_path_str(impl_def_id.to_def_id()) + ); + + if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) { + let param_env = ty::ParamEnv::reveal_all(); + let trait_ref = tcx.normalize_erasing_regions(param_env, trait_ref); + let overridden_methods: FxHashSet<_> = + impl_.items.iter().map(|iiref| iiref.ident.normalize_to_macros_2_0()).collect(); + for method in tcx.provided_trait_methods(trait_ref.def_id) { + if overridden_methods.contains(&method.ident.normalize_to_macros_2_0()) { + continue; + } + + if tcx.generics_of(method.def_id).own_requires_monomorphization() { + continue; + } + + let substs = + InternalSubsts::for_item(tcx, method.def_id, |param, _| match param.kind { + GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { + trait_ref.substs[param.index as usize] + } + }); + let instance = ty::Instance::resolve(tcx, param_env, method.def_id, substs) + .unwrap() + .unwrap(); + + let mono_item = create_fn_mono_item(tcx, instance, DUMMY_SP); + if mono_item.node.is_instantiable(tcx) && should_codegen_locally(tcx, &instance) + { + output.push(mono_item); + } + } + } + } + _ => bug!(), + } +} + +/// Scans the miri alloc in order to find function calls, closures, and drop-glue. +fn collect_miri<'tcx>( + tcx: TyCtxt<'tcx>, + alloc_id: AllocId, + output: &mut Vec>>, +) { + match tcx.global_alloc(alloc_id) { + GlobalAlloc::Static(def_id) => { + assert!(!tcx.is_thread_local_static(def_id)); + let instance = Instance::mono(tcx, def_id); + if should_codegen_locally(tcx, &instance) { + trace!("collecting static {:?}", def_id); + output.push(dummy_spanned(MonoItem::Static(def_id))); + } + } + GlobalAlloc::Memory(alloc) => { + trace!("collecting {:?} with {:#?}", alloc_id, alloc); + for &((), inner) in alloc.relocations().values() { + rustc_data_structures::stack::ensure_sufficient_stack(|| { + collect_miri(tcx, inner, output); + }); + } + } + GlobalAlloc::Function(fn_instance) => { + if should_codegen_locally(tcx, &fn_instance) { + trace!("collecting {:?} with {:#?}", alloc_id, fn_instance); + output.push(create_fn_mono_item(tcx, fn_instance, DUMMY_SP)); + } + } + } +} + +/// Scans the MIR in order to find function calls, closures, and drop-glue. +fn collect_neighbours<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + output: &mut Vec>>, +) { + debug!("collect_neighbours: {:?}", instance.def_id()); + let body = tcx.instance_mir(instance.def); + + MirNeighborCollector { tcx, body: &body, output, instance }.visit_body(&body); +} + +fn collect_const_value<'tcx>( + tcx: TyCtxt<'tcx>, + value: ConstValue<'tcx>, + output: &mut Vec>>, +) { + match value { + ConstValue::Scalar(Scalar::Ptr(ptr)) => collect_miri(tcx, ptr.alloc_id, output), + ConstValue::Slice { data: alloc, start: _, end: _ } | ConstValue::ByRef { alloc, .. } => { + for &((), id) in alloc.relocations().values() { + collect_miri(tcx, id, output); + } + } + _ => {} + } +} diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 0ea3a18ca34fa..710803fead242 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -147,7 +147,8 @@ pub fn is_const_evaluatable<'cx, 'tcx>( // and hopefully soon change this to an error. // // See #74595 for more details about this. - let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span)); + let concrete = + infcx.const_eval_resolve(param_env.with_reveal_selection(), uv.expand(), Some(span)); if concrete.is_ok() && uv.substs(infcx.tcx).definitely_has_param_types_or_consts(infcx.tcx) { match infcx.tcx.def_kind(uv.def.did) { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index b5c5724f56edc..19ce6c3a44085 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -795,11 +795,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let violations = self.tcx.object_safety_violations(did); report_object_safety_error(self.tcx, span, did, violations) } - SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsInfer) => { bug!( "MentionsInfer should have been handled in `traits/fulfill.rs` or `traits/select/mod.rs`" ) + ConstEvalFailure(ErrorHandled::Silent) => { + tcx.sess.struct_span_err(span, "failed to evaluate the given constant") } SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsParam) => { if !self.tcx.features().generic_const_exprs { diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 42e3f0db15e53..cb5798b188c64 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -625,6 +625,11 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { "ConstEquate: const_eval_resolve returned an unexpected error" ) } + (Err(ErrorHandled::Silent), _) | (_, Err(ErrorHandled::Silent)) => { + ProcessResult::Error(CodeSelectionError(ConstEvalFailure( + ErrorHandled::Silent, + ))) + } (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => { if c1.has_infer_types_or_consts() || c2.has_infer_types_or_consts() { ProcessResult::Unchanged diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 23f615a96185d..96d5a85f8dbe2 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -394,8 +394,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { ty::Opaque(def_id, substs) if !substs.has_escaping_bound_vars() => { // Only normalize `impl Trait` after type-checking, usually in codegen. match self.param_env.reveal() { - Reveal::UserFacing => ty.super_fold_with(self), - + Reveal::UserFacing | Reveal::Selection => ty.super_fold_with(self), Reveal::All => { let recursion_limit = self.tcx().recursion_limit(); if !recursion_limit.value_within_limit(self.depth) { diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 26bacf787e2eb..d91a7c00acfe9 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -210,8 +210,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { ty::Opaque(def_id, substs) if !substs.has_escaping_bound_vars() => { // Only normalize `impl Trait` after type-checking, usually in codegen. match self.param_env.reveal() { - Reveal::UserFacing => ty.try_super_fold_with(self), - + Reveal::UserFacing | Reveal::Selection => ty.try_super_fold_with(self), Reveal::All => { let substs = substs.try_super_fold_with(self)?; let recursion_limit = self.tcx().recursion_limit(); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index fa88c8ee37015..b7c116fa89b97 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -654,7 +654,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let ty::ConstKind::Unevaluated(unevaluated) = c.val { self.infcx .const_eval_resolve( - obligation.param_env, + obligation.param_env.with_reveal_selection(), unevaluated, Some(obligation.cause.span), ) @@ -675,8 +675,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Err(_) => Ok(EvaluatedToErr), } } - (Err(ErrorHandled::Reported(ErrorReported)), _) - | (_, Err(ErrorHandled::Reported(ErrorReported))) => Ok(EvaluatedToErr), + (Err(ErrorHandled::Reported(ErrorReported) | ErrorHandled::Silent), _) + | (_, Err(ErrorHandled::Reported(ErrorReported) | ErrorHandled::Silent)) => { + Ok(EvaluatedToErr) + } (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => { span_bug!( obligation.cause.span(self.tcx()), @@ -2657,7 +2659,11 @@ impl<'o, 'tcx> TraitObligationStackList<'o, 'tcx> { } fn depth(&self) -> usize { - if let Some(head) = self.head { head.depth } else { 0 } + if let Some(head) = self.head { + head.depth + } else { + 0 + } } } diff --git a/src/test/ui/const-generics/generic_const_exprs/silent-selection-err.rs b/src/test/ui/const-generics/generic_const_exprs/silent-selection-err.rs new file mode 100644 index 0000000000000..c292361e1cdfd --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/silent-selection-err.rs @@ -0,0 +1,43 @@ +// run-pass +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + +struct Array; + +trait Matrix { + fn do_it(&self) -> usize; +} + +impl Matrix for Array<0> { + fn do_it(&self) -> usize { + 0 + } +} + +impl Matrix for Array<1> { + fn do_it(&self) -> usize { + 1 + } +} + +impl Matrix for Array<2> { + fn do_it(&self) -> usize { + 2 + } +} + +impl Matrix for Array +where + [u8; N - 3]: Sized, +{ + fn do_it(&self) -> usize { + N + 1 + } +} + +fn main() { + assert_eq!(Array::<0>.do_it(), 0); + assert_eq!(Array::<1>.do_it(), 1); + assert_eq!(Array::<2>.do_it(), 2); + assert_eq!(Array::<3>.do_it(), 4); +} From 10e5f1a2dbb664895eee21564eb651852d9ec32d Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Sun, 24 Jan 2021 15:48:37 +0100 Subject: [PATCH 02/11] update tests --- .../defaults-cyclic-fail.stderr | 10 + ...9-assoc-const-static-recursion-impl.stderr | 10 + ...onst-static-recursion-trait-default.stderr | 10 + ...-assoc-const-static-recursion-trait.stderr | 10 + .../from-sig-fail.rs | 12 + .../from-sig-fail.stderr | 20 ++ .../simple_fail.full.stderr | 17 ++ .../const_evaluatable_checked/simple_fail.rs | 17 ++ .../const-eval/const-eval-overflow-3.rs | 1 + .../const-eval/const-eval-overflow-3.stderr | 8 +- ...nst-pointer-values-in-various-types.stderr | 237 ++++++++++++++++++ .../ui/consts/const-eval/infinite_loop.rs | 2 +- .../ui/consts/const-eval/infinite_loop.stderr | 15 +- .../ui/consts/const-eval/issue-49296.stderr | 8 + src/test/ui/consts/const-eval/issue-52475.rs | 2 +- .../ui/consts/const-eval/issue-52475.stderr | 15 +- src/test/ui/consts/const-size_of-cycle.stderr | 5 + src/test/ui/consts/issue-36163.stderr | 10 + src/test/ui/consts/issue-44415.stderr | 5 + src/test/ui/consts/issue-52432.rs | 10 + src/test/ui/consts/issue-52432.stderr | 28 +++ .../recursive-zst-static.default.stderr | 5 + .../recursive-zst-static.unleash.stderr | 5 + .../write-to-static-mut-in-static.stderr | 5 + src/test/ui/issues/issue-17252.stderr | 5 + src/test/ui/issues/issue-23302-1.stderr | 5 + src/test/ui/issues/issue-23302-2.stderr | 5 + src/test/ui/issues/issue-23302-3.stderr | 10 + src/test/ui/mir/issue-80742.rs | 35 +++ src/test/ui/mir/issue-80742.stderr | 54 ++++ .../recursive-static-definition.stderr | 5 + .../self-in-enum-definition.stderr | 5 + 32 files changed, 586 insertions(+), 5 deletions(-) create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/from-sig-fail.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/from-sig-fail.stderr create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/simple_fail.full.stderr create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs create mode 100644 src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr create mode 100644 src/test/ui/consts/issue-52432.rs create mode 100644 src/test/ui/consts/issue-52432.stderr create mode 100644 src/test/ui/mir/issue-80742.rs create mode 100644 src/test/ui/mir/issue-80742.stderr diff --git a/src/test/ui/associated-consts/defaults-cyclic-fail.stderr b/src/test/ui/associated-consts/defaults-cyclic-fail.stderr index 3fb4ab72fe6d6..9ae202bf5368b 100644 --- a/src/test/ui/associated-consts/defaults-cyclic-fail.stderr +++ b/src/test/ui/associated-consts/defaults-cyclic-fail.stderr @@ -10,6 +10,11 @@ note: ...which requires simplifying constant for the type system `Tr::A`... | LL | const A: u8 = Self::B; | ^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires simplifying constant for the type system `Tr::A`... + --> $DIR/defaults-cyclic-fail.rs:6:5 + | +LL | const A: u8 = Self::B; + | ^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `Tr::A`... --> $DIR/defaults-cyclic-fail.rs:6:5 | @@ -26,6 +31,11 @@ note: ...which requires simplifying constant for the type system `Tr::B`... | LL | const B: u8 = Self::A; | ^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires simplifying constant for the type system `Tr::B`... + --> $DIR/defaults-cyclic-fail.rs:8:5 + | +LL | const B: u8 = Self::A; + | ^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `Tr::B`... --> $DIR/defaults-cyclic-fail.rs:8:5 | diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr index 61b16cb9d5811..aaa9b63c8f5fd 100644 --- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr +++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr @@ -9,6 +9,11 @@ note: ...which requires simplifying constant for the type system `IMPL_REF_BAR`. | LL | const IMPL_REF_BAR: u32 = GlobalImplRef::BAR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires simplifying constant for the type system `IMPL_REF_BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:1 + | +LL | const IMPL_REF_BAR: u32 = GlobalImplRef::BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `IMPL_REF_BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:1 | @@ -25,6 +30,11 @@ note: ...which requires simplifying constant for the type system `::BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:5 + | +LL | const BAR: u32 = IMPL_REF_BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `::BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:5 | diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr index 494dc0c0ed447..a65832b096fbc 100644 --- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr +++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr @@ -9,6 +9,11 @@ note: ...which requires simplifying constant for the type system `DEFAULT_REF_BA | LL | const DEFAULT_REF_BAR: u32 = ::BAR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires simplifying constant for the type system `DEFAULT_REF_BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:11:1 + | +LL | const DEFAULT_REF_BAR: u32 = ::BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `DEFAULT_REF_BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:11:1 | @@ -25,6 +30,11 @@ note: ...which requires simplifying constant for the type system `FooDefault::BA | LL | const BAR: u32 = DEFAULT_REF_BAR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires simplifying constant for the type system `FooDefault::BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:8:5 + | +LL | const BAR: u32 = DEFAULT_REF_BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `FooDefault::BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:8:5 | diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr index 4ff253bffcb32..1fea9c0dabaf4 100644 --- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr +++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr @@ -9,6 +9,11 @@ note: ...which requires simplifying constant for the type system `TRAIT_REF_BAR` | LL | const TRAIT_REF_BAR: u32 = ::BAR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires simplifying constant for the type system `TRAIT_REF_BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:7:1 + | +LL | const TRAIT_REF_BAR: u32 = ::BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `TRAIT_REF_BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:7:1 | @@ -25,6 +30,11 @@ note: ...which requires simplifying constant for the type system `::BAR`... + --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:12:5 + | +LL | const BAR: u32 = TRAIT_REF_BAR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `::BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:12:5 | diff --git a/src/test/ui/const-generics/const_evaluatable_checked/from-sig-fail.rs b/src/test/ui/const-generics/const_evaluatable_checked/from-sig-fail.rs new file mode 100644 index 0000000000000..87fdc63f6393e --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/from-sig-fail.rs @@ -0,0 +1,12 @@ +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + +fn test() -> [u8; N - 1] { + todo!() +} + +fn main() { + test::<0>(); + //~^ ERROR failed to evaluate the given constant + //~| ERROR failed to evaluate the given constant +} diff --git a/src/test/ui/const-generics/const_evaluatable_checked/from-sig-fail.stderr b/src/test/ui/const-generics/const_evaluatable_checked/from-sig-fail.stderr new file mode 100644 index 0000000000000..e94545235d115 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/from-sig-fail.stderr @@ -0,0 +1,20 @@ +error: failed to evaluate the given constant + --> $DIR/from-sig-fail.rs:9:5 + | +LL | fn test() -> [u8; N - 1] { + | ----- required by this bound in `test` +... +LL | test::<0>(); + | ^^^^^^^^^ + +error: failed to evaluate the given constant + --> $DIR/from-sig-fail.rs:9:5 + | +LL | fn test() -> [u8; N - 1] { + | ----- required by this bound in `test` +... +LL | test::<0>(); + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.full.stderr b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.full.stderr new file mode 100644 index 0000000000000..c38c58897f305 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.full.stderr @@ -0,0 +1,17 @@ +error: failed to evaluate the given constant + --> $DIR/simple_fail.rs:14:5 + | +LL | fn test() -> Arr where Arr: Sized { + | ------ required by this bound in `test` +... +LL | test::<0>(); + | ^^^^^^^^^ + +error: failed to evaluate the given constant + --> $DIR/simple_fail.rs:14:5 + | +LL | test::<0>(); + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs new file mode 100644 index 0000000000000..f673b918fbcdd --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs @@ -0,0 +1,17 @@ +// revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![feature(const_evaluatable_checked)] +#![allow(incomplete_features)] + +type Arr = [u8; N - 1]; +//[min]~^ ERROR generic parameters may not be used in const operations + +fn test() -> Arr where Arr: Sized { + todo!() +} + +fn main() { + test::<0>(); + //[full]~^ ERROR failed to evaluate the given constant + //[full]~| ERROR failed to evaluate the given constant +} diff --git a/src/test/ui/consts/const-eval/const-eval-overflow-3.rs b/src/test/ui/consts/const-eval/const-eval-overflow-3.rs index bcc966dc9621c..89e0011b7d10c 100644 --- a/src/test/ui/consts/const-eval/const-eval-overflow-3.rs +++ b/src/test/ui/consts/const-eval/const-eval-overflow-3.rs @@ -17,6 +17,7 @@ const A_I8_I : [u32; (i8::MAX as usize) + 1] = [0; (i8::MAX + 1) as usize]; //~^ ERROR evaluation of constant value failed +//~| ERROR failed to evaluate the given constant fn main() { foo(&A_I8_I[..]); diff --git a/src/test/ui/consts/const-eval/const-eval-overflow-3.stderr b/src/test/ui/consts/const-eval/const-eval-overflow-3.stderr index 73f421b5b1465..2d1cd5459125b 100644 --- a/src/test/ui/consts/const-eval/const-eval-overflow-3.stderr +++ b/src/test/ui/consts/const-eval/const-eval-overflow-3.stderr @@ -4,6 +4,12 @@ error[E0080]: evaluation of constant value failed LL | = [0; (i8::MAX + 1) as usize]; | ^^^^^^^^^^^^^ attempt to compute `i8::MAX + 1_i8`, which would overflow -error: aborting due to previous error +error: failed to evaluate the given constant + --> $DIR/const-eval-overflow-3.rs:18:11 + | +LL | = [0; (i8::MAX + 1) as usize]; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr new file mode 100644 index 0000000000000..72e54f8ec21da --- /dev/null +++ b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr @@ -0,0 +1,237 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-pointer-values-in-various-types.rs:25:5 + | +LL | const I32_REF_USIZE_UNION: usize = unsafe { Nonsense { int_32_ref: &3 }.u }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc2, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:28:43 + | +LL | const I32_REF_U8_UNION: u8 = unsafe { Nonsense { int_32_ref: &3 }.uint_8 }; + | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = note: `#[deny(const_err)]` on by default + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:31:45 + | +LL | const I32_REF_U16_UNION: u16 = unsafe { Nonsense { int_32_ref: &3 }.uint_16 }; + | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:34:45 + | +LL | const I32_REF_U32_UNION: u32 = unsafe { Nonsense { int_32_ref: &3 }.uint_32 }; + | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-pointer-values-in-various-types.rs:37:5 + | +LL | const I32_REF_U64_UNION: u64 = unsafe { Nonsense { int_32_ref: &3 }.uint_64 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc24, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-pointer-values-in-various-types.rs:40:5 + | +LL | const I32_REF_U128_UNION: u128 = unsafe { Nonsense { int_32_ref: &3 }.uint_128 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:43:43 + | +LL | const I32_REF_I8_UNION: i8 = unsafe { Nonsense { int_32_ref: &3 }.int_8 }; + | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:46:45 + | +LL | const I32_REF_I16_UNION: i16 = unsafe { Nonsense { int_32_ref: &3 }.int_16 }; + | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:49:45 + | +LL | const I32_REF_I32_UNION: i32 = unsafe { Nonsense { int_32_ref: &3 }.int_32 }; + | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-pointer-values-in-various-types.rs:52:5 + | +LL | const I32_REF_I64_UNION: i64 = unsafe { Nonsense { int_32_ref: &3 }.int_64 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc50, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-pointer-values-in-various-types.rs:55:5 + | +LL | const I32_REF_I128_UNION: i128 = unsafe { Nonsense { int_32_ref: &3 }.int_128 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:58:45 + | +LL | const I32_REF_F32_UNION: f32 = unsafe { Nonsense { int_32_ref: &3 }.float_32 }; + | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-pointer-values-in-various-types.rs:61:5 + | +LL | const I32_REF_F64_UNION: f64 = unsafe { Nonsense { int_32_ref: &3 }.float_64 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc64, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:64:47 + | +LL | const I32_REF_BOOL_UNION: bool = unsafe { Nonsense { int_32_ref: &3 }.truthy_falsey }; + | ------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:67:47 + | +LL | const I32_REF_CHAR_UNION: char = unsafe { Nonsense { int_32_ref: &3 }.character }; + | ------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:70:39 + | +LL | const STR_U8_UNION: u8 = unsafe { Nonsense { stringy: "3" }.uint_8 }; + | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:73:41 + | +LL | const STR_U16_UNION: u16 = unsafe { Nonsense { stringy: "3" }.uint_16 }; + | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:76:41 + | +LL | const STR_U32_UNION: u32 = unsafe { Nonsense { stringy: "3" }.uint_32 }; + | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-pointer-values-in-various-types.rs:79:5 + | +LL | const STR_U64_UNION: u64 = unsafe { Nonsense { stringy: "3" }.uint_64 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc98, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:82:43 + | +LL | const STR_U128_UNION: u128 = unsafe { Nonsense { stringy: "3" }.uint_128 }; + | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:85:39 + | +LL | const STR_I8_UNION: i8 = unsafe { Nonsense { stringy: "3" }.int_8 }; + | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:88:41 + | +LL | const STR_I16_UNION: i16 = unsafe { Nonsense { stringy: "3" }.int_16 }; + | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:91:41 + | +LL | const STR_I32_UNION: i32 = unsafe { Nonsense { stringy: "3" }.int_32 }; + | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-pointer-values-in-various-types.rs:94:5 + | +LL | const STR_I64_UNION: i64 = unsafe { Nonsense { stringy: "3" }.int_64 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc125, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:97:43 + | +LL | const STR_I128_UNION: i128 = unsafe { Nonsense { stringy: "3" }.int_128 }; + | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:100:41 + | +LL | const STR_F32_UNION: f32 = unsafe { Nonsense { stringy: "3" }.float_32 }; + | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-pointer-values-in-various-types.rs:103:5 + | +LL | const STR_F64_UNION: f64 = unsafe { Nonsense { stringy: "3" }.float_64 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc140, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:106:43 + | +LL | const STR_BOOL_UNION: bool = unsafe { Nonsense { stringy: "3" }.truthy_falsey }; + | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:109:43 + | +LL | const STR_CHAR_UNION: char = unsafe { Nonsense { stringy: "3" }.character }; + | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + +error: aborting due to 29 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/infinite_loop.rs b/src/test/ui/consts/const-eval/infinite_loop.rs index 14a573ccf5ac6..3c40c337cd005 100644 --- a/src/test/ui/consts/const-eval/infinite_loop.rs +++ b/src/test/ui/consts/const-eval/infinite_loop.rs @@ -1,7 +1,7 @@ fn main() { // Tests the Collatz conjecture with an incorrect base case (0 instead of 1). // The value of `n` will loop indefinitely (4 - 2 - 1 - 4). - let _ = [(); { + let _ = [(); { //~ ERROR failed to evaluate the given constant let mut n = 113383; // #20 in https://oeis.org/A006884 while n != 0 { n = if n % 2 == 0 { n/2 } else { 3*n + 1 }; diff --git a/src/test/ui/consts/const-eval/infinite_loop.stderr b/src/test/ui/consts/const-eval/infinite_loop.stderr index 3b5a0f22f28be..b30a53619b574 100644 --- a/src/test/ui/consts/const-eval/infinite_loop.stderr +++ b/src/test/ui/consts/const-eval/infinite_loop.stderr @@ -1,9 +1,22 @@ +error: failed to evaluate the given constant + --> $DIR/infinite_loop.rs:4:18 + | +LL | let _ = [(); { + | __________________^ +LL | | let mut n = 113383; // #20 in https://oeis.org/A006884 +LL | | while n != 0 { +LL | | n = if n % 2 == 0 { n/2 } else { 3*n + 1 }; +... | +LL | | n +LL | | }]; + | |_____^ + error[E0080]: evaluation of constant value failed --> $DIR/infinite_loop.rs:7:20 | LL | n = if n % 2 == 0 { n/2 } else { 3*n + 1 }; | ^^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`) -error: aborting due to previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/issue-49296.stderr b/src/test/ui/consts/const-eval/issue-49296.stderr index bc3074b10bee6..5fc869d4ce137 100644 --- a/src/test/ui/consts/const-eval/issue-49296.stderr +++ b/src/test/ui/consts/const-eval/issue-49296.stderr @@ -2,7 +2,15 @@ error[E0080]: evaluation of constant value failed --> $DIR/issue-49296.rs:11:16 | LL | const X: u64 = *wat(42); +<<<<<<< HEAD | ^^^^^^^^ pointer to alloc2 was dereferenced after this allocation got freed +======= + | ---------------^^^^^^^^- + | | + | pointer to alloc4 was dereferenced after this allocation got freed + | + = note: `#[deny(const_err)]` on by default +>>>>>>> 9ee0d801b76 (update tests) error: aborting due to previous error diff --git a/src/test/ui/consts/const-eval/issue-52475.rs b/src/test/ui/consts/const-eval/issue-52475.rs index ce65407bbab0b..6b8b42d417b35 100644 --- a/src/test/ui/consts/const-eval/issue-52475.rs +++ b/src/test/ui/consts/const-eval/issue-52475.rs @@ -1,5 +1,5 @@ fn main() { - let _ = [(); { + let _ = [(); { //~ ERROR failed to evaluate the given constant let mut x = &0; let mut n = 0; while n < 5 { diff --git a/src/test/ui/consts/const-eval/issue-52475.stderr b/src/test/ui/consts/const-eval/issue-52475.stderr index 8536ff02c6dae..5679f51c9ea54 100644 --- a/src/test/ui/consts/const-eval/issue-52475.stderr +++ b/src/test/ui/consts/const-eval/issue-52475.stderr @@ -1,9 +1,22 @@ +error: failed to evaluate the given constant + --> $DIR/issue-52475.rs:2:18 + | +LL | let _ = [(); { + | __________________^ +LL | | let mut x = &0; +LL | | let mut n = 0; +LL | | while n < 5 { +... | +LL | | 0 +LL | | }]; + | |_____^ + error[E0080]: evaluation of constant value failed --> $DIR/issue-52475.rs:6:17 | LL | n = (n + 1) % 5; | ^^^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`) -error: aborting due to previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-size_of-cycle.stderr b/src/test/ui/consts/const-size_of-cycle.stderr index 1067eb003f7c7..0b1b6f0caf192 100644 --- a/src/test/ui/consts/const-size_of-cycle.stderr +++ b/src/test/ui/consts/const-size_of-cycle.stderr @@ -9,6 +9,11 @@ note: ...which requires simplifying constant for the type system `Foo::bytes::{c | LL | bytes: [u8; std::mem::size_of::()] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires simplifying constant for the type system `Foo::bytes::{constant#0}`... + --> $DIR/const-size_of-cycle.rs:4:17 + | +LL | bytes: [u8; std::mem::size_of::()] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`... --> $DIR/const-size_of-cycle.rs:4:17 | diff --git a/src/test/ui/consts/issue-36163.stderr b/src/test/ui/consts/issue-36163.stderr index 113f86cf0f99f..96ab7033e2501 100644 --- a/src/test/ui/consts/issue-36163.stderr +++ b/src/test/ui/consts/issue-36163.stderr @@ -9,6 +9,11 @@ note: ...which requires simplifying constant for the type system `Foo::B::{const | LL | B = A, | ^ +note: ...which requires simplifying constant for the type system `Foo::B::{constant#0}`... + --> $DIR/issue-36163.rs:4:9 + | +LL | B = A, + | ^ note: ...which requires const-evaluating + checking `Foo::B::{constant#0}`... --> $DIR/issue-36163.rs:4:9 | @@ -25,6 +30,11 @@ note: ...which requires simplifying constant for the type system `A`... | LL | const A: isize = Foo::B as isize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires simplifying constant for the type system `A`... + --> $DIR/issue-36163.rs:1:1 + | +LL | const A: isize = Foo::B as isize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `A`... --> $DIR/issue-36163.rs:1:1 | diff --git a/src/test/ui/consts/issue-44415.stderr b/src/test/ui/consts/issue-44415.stderr index 9e3db5ce9a402..6a8e09cfcb971 100644 --- a/src/test/ui/consts/issue-44415.stderr +++ b/src/test/ui/consts/issue-44415.stderr @@ -9,6 +9,11 @@ note: ...which requires simplifying constant for the type system `Foo::bytes::{c | LL | bytes: [u8; unsafe { intrinsics::size_of::() }], | ^^^^^^ +note: ...which requires simplifying constant for the type system `Foo::bytes::{constant#0}`... + --> $DIR/issue-44415.rs:6:17 + | +LL | bytes: [u8; unsafe { intrinsics::size_of::() }], + | ^^^^^^ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`... --> $DIR/issue-44415.rs:6:17 | diff --git a/src/test/ui/consts/issue-52432.rs b/src/test/ui/consts/issue-52432.rs new file mode 100644 index 0000000000000..6be782f95ad37 --- /dev/null +++ b/src/test/ui/consts/issue-52432.rs @@ -0,0 +1,10 @@ +#![feature(const_raw_ptr_to_usize_cast)] + +fn main() { + [(); &(static |x| {}) as *const _ as usize]; + //~^ ERROR: closures cannot be static + //~| ERROR: type annotations needed + [(); &(static || {}) as *const _ as usize]; + //~^ ERROR: closures cannot be static + //~| ERROR: failed to evaluate the given constant +} diff --git a/src/test/ui/consts/issue-52432.stderr b/src/test/ui/consts/issue-52432.stderr new file mode 100644 index 0000000000000..2710a72f3a105 --- /dev/null +++ b/src/test/ui/consts/issue-52432.stderr @@ -0,0 +1,28 @@ +error[E0697]: closures cannot be static + --> $DIR/issue-52432.rs:4:12 + | +LL | [(); &(static |x| {}) as *const _ as usize]; + | ^^^^^^^^^^ + +error[E0697]: closures cannot be static + --> $DIR/issue-52432.rs:7:12 + | +LL | [(); &(static || {}) as *const _ as usize]; + | ^^^^^^^^^ + +error[E0282]: type annotations needed + --> $DIR/issue-52432.rs:4:20 + | +LL | [(); &(static |x| {}) as *const _ as usize]; + | ^ consider giving this closure parameter a type + +error: failed to evaluate the given constant + --> $DIR/issue-52432.rs:7:10 + | +LL | [(); &(static || {}) as *const _ as usize]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0282, E0697. +For more information about an error, try `rustc --explain E0282`. diff --git a/src/test/ui/consts/recursive-zst-static.default.stderr b/src/test/ui/consts/recursive-zst-static.default.stderr index 03f8f5c5a0e5d..8ddc9fe4f5ff9 100644 --- a/src/test/ui/consts/recursive-zst-static.default.stderr +++ b/src/test/ui/consts/recursive-zst-static.default.stderr @@ -7,6 +7,11 @@ LL | static FOO: () = FOO; note: ...which requires const-evaluating + checking `FOO`... --> $DIR/recursive-zst-static.rs:10:1 | +LL | static FOO: () = FOO; + | ^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating + checking `FOO`... + --> $DIR/recursive-zst-static.rs:10:1 + | LL | static FOO: () = FOO; | ^^^^^^^^^^^^^^^^^^^^^ = note: ...which again requires const-evaluating + checking `FOO`, completing the cycle diff --git a/src/test/ui/consts/recursive-zst-static.unleash.stderr b/src/test/ui/consts/recursive-zst-static.unleash.stderr index 03f8f5c5a0e5d..8ddc9fe4f5ff9 100644 --- a/src/test/ui/consts/recursive-zst-static.unleash.stderr +++ b/src/test/ui/consts/recursive-zst-static.unleash.stderr @@ -7,6 +7,11 @@ LL | static FOO: () = FOO; note: ...which requires const-evaluating + checking `FOO`... --> $DIR/recursive-zst-static.rs:10:1 | +LL | static FOO: () = FOO; + | ^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating + checking `FOO`... + --> $DIR/recursive-zst-static.rs:10:1 + | LL | static FOO: () = FOO; | ^^^^^^^^^^^^^^^^^^^^^ = note: ...which again requires const-evaluating + checking `FOO`, completing the cycle diff --git a/src/test/ui/consts/write-to-static-mut-in-static.stderr b/src/test/ui/consts/write-to-static-mut-in-static.stderr index 789919bd1668d..6493ddabc4ea0 100644 --- a/src/test/ui/consts/write-to-static-mut-in-static.stderr +++ b/src/test/ui/consts/write-to-static-mut-in-static.stderr @@ -13,6 +13,11 @@ LL | pub static mut C: u32 = unsafe { C = 1; 0 }; note: ...which requires const-evaluating + checking `C`... --> $DIR/write-to-static-mut-in-static.rs:5:1 | +LL | pub static mut C: u32 = unsafe { C = 1; 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating + checking `C`... + --> $DIR/write-to-static-mut-in-static.rs:5:1 + | LL | pub static mut C: u32 = unsafe { C = 1; 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: ...which again requires const-evaluating + checking `C`, completing the cycle diff --git a/src/test/ui/issues/issue-17252.stderr b/src/test/ui/issues/issue-17252.stderr index 1148577016ab4..2372bbb5ca3d7 100644 --- a/src/test/ui/issues/issue-17252.stderr +++ b/src/test/ui/issues/issue-17252.stderr @@ -10,6 +10,11 @@ note: ...which requires simplifying constant for the type system `FOO`... | LL | const FOO: usize = FOO; | ^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires simplifying constant for the type system `FOO`... + --> $DIR/issue-17252.rs:1:1 + | +LL | const FOO: usize = FOO; + | ^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `FOO`... --> $DIR/issue-17252.rs:1:1 | diff --git a/src/test/ui/issues/issue-23302-1.stderr b/src/test/ui/issues/issue-23302-1.stderr index d3a1993536a00..4b5a33057f50d 100644 --- a/src/test/ui/issues/issue-23302-1.stderr +++ b/src/test/ui/issues/issue-23302-1.stderr @@ -9,6 +9,11 @@ note: ...which requires simplifying constant for the type system `X::A::{constan | LL | A = X::A as isize, | ^^^^^^^^^^^^^ +note: ...which requires simplifying constant for the type system `X::A::{constant#0}`... + --> $DIR/issue-23302-1.rs:4:9 + | +LL | A = X::A as isize, + | ^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `X::A::{constant#0}`... --> $DIR/issue-23302-1.rs:4:9 | diff --git a/src/test/ui/issues/issue-23302-2.stderr b/src/test/ui/issues/issue-23302-2.stderr index d3b78ea1af5f5..e2a0d977cc655 100644 --- a/src/test/ui/issues/issue-23302-2.stderr +++ b/src/test/ui/issues/issue-23302-2.stderr @@ -9,6 +9,11 @@ note: ...which requires simplifying constant for the type system `Y::A::{constan | LL | A = Y::B as isize, | ^^^^^^^^^^^^^ +note: ...which requires simplifying constant for the type system `Y::A::{constant#0}`... + --> $DIR/issue-23302-2.rs:4:9 + | +LL | A = Y::B as isize, + | ^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `Y::A::{constant#0}`... --> $DIR/issue-23302-2.rs:4:9 | diff --git a/src/test/ui/issues/issue-23302-3.stderr b/src/test/ui/issues/issue-23302-3.stderr index 5233b832ecc79..66ba9c2f68e7c 100644 --- a/src/test/ui/issues/issue-23302-3.stderr +++ b/src/test/ui/issues/issue-23302-3.stderr @@ -9,6 +9,11 @@ note: ...which requires simplifying constant for the type system `A`... | LL | const A: i32 = B; | ^^^^^^^^^^^^^^^^^ +note: ...which requires simplifying constant for the type system `A`... + --> $DIR/issue-23302-3.rs:1:1 + | +LL | const A: i32 = B; + | ^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `A`... --> $DIR/issue-23302-3.rs:1:1 | @@ -25,6 +30,11 @@ note: ...which requires simplifying constant for the type system `B`... | LL | const B: i32 = A; | ^^^^^^^^^^^^^^^^^ +note: ...which requires simplifying constant for the type system `B`... + --> $DIR/issue-23302-3.rs:3:1 + | +LL | const B: i32 = A; + | ^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `B`... --> $DIR/issue-23302-3.rs:3:1 | diff --git a/src/test/ui/mir/issue-80742.rs b/src/test/ui/mir/issue-80742.rs new file mode 100644 index 0000000000000..d82f73fc2a189 --- /dev/null +++ b/src/test/ui/mir/issue-80742.rs @@ -0,0 +1,35 @@ +// check-fail + +// This test used to cause an ICE in rustc_mir::interpret::step::eval_rvalue_into_place + +#![allow(incomplete_features)] +#![feature(const_evaluatable_checked)] +#![feature(const_generics)] + +use std::fmt::Debug; +use std::marker::PhantomData; +use std::mem::size_of; + +struct Inline +where + [u8; size_of::() + 1]: , +{ + _phantom: PhantomData, + buf: [u8; size_of::() + 1], +} + +impl Inline +where + [u8; size_of::() + 1]: , +{ + pub fn new(val: T) -> Inline { + todo!() + } +} + +fn main() { + let dst = Inline::::new(0); + //~^ ERROR failed to evaluate the given constant + //~| ERROR the size for values of type + //~| ERROR no function or associated item +} diff --git a/src/test/ui/mir/issue-80742.stderr b/src/test/ui/mir/issue-80742.stderr new file mode 100644 index 0000000000000..61992f3b9c407 --- /dev/null +++ b/src/test/ui/mir/issue-80742.stderr @@ -0,0 +1,54 @@ +error[E0599]: no function or associated item named `new` found for struct `Inline` in the current scope + --> $DIR/issue-80742.rs:31:36 + | +LL | / struct Inline +LL | | where +LL | | [u8; size_of::() + 1]: , +LL | | { +LL | | _phantom: PhantomData, +LL | | buf: [u8; size_of::() + 1], +LL | | } + | |_- function or associated item `new` not found for this +... +LL | let dst = Inline::::new(0); + | ^^^ function or associated item not found in `Inline` + | + ::: $SRC_DIR/core/src/fmt/mod.rs:LL:COL + | +LL | pub trait Debug { + | --------------- doesn't satisfy `dyn Debug: Sized` + | + = note: the method `new` exists but the following trait bounds were not satisfied: + `dyn Debug: Sized` + +error[E0277]: the size for values of type `dyn Debug` cannot be known at compilation time + --> $DIR/issue-80742.rs:31:15 + | +LL | struct Inline + | - required by this bound in `Inline` +... +LL | let dst = Inline::::new(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `dyn Debug` +help: consider relaxing the implicit `Sized` restriction + | +LL | struct Inline + | ^^^^^^^^ + +error: failed to evaluate the given constant + --> $DIR/issue-80742.rs:31:15 + | +LL | struct Inline + | ------ required by a bound in this +LL | where +LL | [u8; size_of::() + 1]: , + | ------------------ required by this bound in `Inline` +... +LL | let dst = Inline::::new(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0599. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/recursion/recursive-static-definition.stderr b/src/test/ui/recursion/recursive-static-definition.stderr index ee73b026a0b75..42bfa76000b1e 100644 --- a/src/test/ui/recursion/recursive-static-definition.stderr +++ b/src/test/ui/recursion/recursive-static-definition.stderr @@ -7,6 +7,11 @@ LL | pub static FOO: u32 = FOO; note: ...which requires const-evaluating + checking `FOO`... --> $DIR/recursive-static-definition.rs:1:1 | +LL | pub static FOO: u32 = FOO; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const-evaluating + checking `FOO`... + --> $DIR/recursive-static-definition.rs:1:1 + | LL | pub static FOO: u32 = FOO; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: ...which again requires const-evaluating + checking `FOO`, completing the cycle diff --git a/src/test/ui/type-alias-enum-variants/self-in-enum-definition.stderr b/src/test/ui/type-alias-enum-variants/self-in-enum-definition.stderr index 277f4e8424030..211086e5ec528 100644 --- a/src/test/ui/type-alias-enum-variants/self-in-enum-definition.stderr +++ b/src/test/ui/type-alias-enum-variants/self-in-enum-definition.stderr @@ -9,6 +9,11 @@ note: ...which requires simplifying constant for the type system `Alpha::V3::{co | LL | V3 = Self::V1 {} as u8 + 2, | ^^^^^^^^ +note: ...which requires simplifying constant for the type system `Alpha::V3::{constant#0}`... + --> $DIR/self-in-enum-definition.rs:5:10 + | +LL | V3 = Self::V1 {} as u8 + 2, + | ^^^^^^^^ note: ...which requires const-evaluating + checking `Alpha::V3::{constant#0}`... --> $DIR/self-in-enum-definition.rs:5:10 | From bbd8ef9de7794a1a9ca7aee2afc5d752d656df43 Mon Sep 17 00:00:00 2001 From: lcnr Date: Sun, 24 Jan 2021 16:22:07 +0100 Subject: [PATCH 03/11] typo Co-authored-by: bjorn3 --- compiler/rustc_codegen_cranelift/src/constant.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 66679f6fe4696..8b19cbf74a054 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -67,7 +67,7 @@ pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool { ErrorHandled::Silent => { span_bug!( constant.span, - "codgen encountered silent error", + "codegen encountered silent error", ); } } From 80955cd02530af534ab2242d3f4c4a1f03d33ab6 Mon Sep 17 00:00:00 2001 From: b-naber Date: Sat, 8 Jan 2022 11:06:21 +0100 Subject: [PATCH 04/11] rebase --- compiler/rustc_middle/src/ty/adt.rs | 1 + compiler/rustc_monomorphize/src/collector.rs | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 5cde54c9328d1..2151534e233a3 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -426,6 +426,7 @@ impl<'tcx> AdtDef { "enum discriminant evaluation failed" } ErrorHandled::TooGeneric => "enum discriminant depends on generics", + ErrorHandled::Silent => return None, }; tcx.sess.delay_span_bug(tcx.def_span(expr_did), msg); None diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index b1fa9041342a6..8dbc153283576 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -731,6 +731,10 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { "collection encountered polymorphic constant: {:?}", literal ), + Err(ErrorHandled::Silent) => span_bug!( + self.body.source_info(location).span, + "silent error emitted during collection", + ), } } _ => return, @@ -763,6 +767,10 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { "collection encountered polymorphic constant: {}", substituted_constant ), + Err(ErrorHandled::Silent) => span_bug!( + self.body.source_info(location).span, + "silent error emitted during collection", + ), } } _ => {} From 8cb36a02b4e3cb2782bece06ea2f856465a5f78b Mon Sep 17 00:00:00 2001 From: b-naber Date: Fri, 7 Jan 2022 20:16:52 +0100 Subject: [PATCH 05/11] introduce Reveal::Selection and deduplication for eval const queries --- .../rustc_middle/src/mir/interpret/mod.rs | 212 ++- compiler/rustc_middle/src/ty/consts/kind.rs | 14 +- compiler/rustc_middle/src/ty/context.rs | 5 + compiler/rustc_middle/src/ty/mod.rs | 37 +- .../rustc_mir/src/const_eval/eval_queries.rs | 453 ------ .../rustc_mir/src/monomorphize/collector.rs | 1265 ----------------- 6 files changed, 243 insertions(+), 1743 deletions(-) delete mode 100644 compiler/rustc_mir/src/const_eval/eval_queries.rs delete mode 100644 compiler/rustc_mir/src/monomorphize/collector.rs diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index b762a10da847a..67e50420e8240 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -95,8 +95,10 @@ mod pointer; mod queries; mod value; +use std::cell::RefCell; use std::convert::TryFrom; use std::fmt; +use std::fmt::Debug; use std::io; use std::io::{Read, Write}; use std::num::{NonZeroU32, NonZeroU64}; @@ -106,10 +108,13 @@ use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::{HashMapExt, Lock}; use rustc_data_structures::tiny_list::TinyList; +use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_macros::HashStable; +use rustc_hir::definitions::DefPathData; +use rustc_middle::traits::Reveal; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_serialize::{Decodable, Encodable}; +use rustc_span::{Pos, Span}; use rustc_target::abi::Endian; use crate::mir; @@ -446,6 +451,88 @@ impl<'tcx> AllocMap<'tcx> { } } +/// What we store about a frame in an interpreter backtrace. +#[derive(Debug)] +pub struct FrameInfo<'tcx> { + pub instance: ty::Instance<'tcx>, + pub span: Span, + pub lint_root: Option, +} + +impl<'tcx> fmt::Display for FrameInfo<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(|tcx| { + if tcx.def_key(self.instance.def_id()).disambiguated_data.data + == DefPathData::ClosureExpr + { + write!(f, "inside closure")?; + } else { + write!(f, "inside `{}`", self.instance)?; + } + if !self.span.is_dummy() { + let sm = tcx.sess.source_map(); + let lo = sm.lookup_char_pos(self.span.lo()); + write!( + f, + " at {}:{}:{}", + sm.filename_for_diagnostics(&lo.file.name), + lo.line, + lo.col.to_usize() + 1 + )?; + } + Ok(()) + }) + } +} + +#[derive(Clone, Copy, Debug)] +pub enum ConstDedupResult { + Selection(T), + UserFacing(T), + All(T), +} + +impl ConstDedupResult { + pub fn new(reveal: Reveal, val: T) -> Self { + match reveal { + Reveal::Selection => ConstDedupResult::Selection(val), + Reveal::UserFacing => ConstDedupResult::UserFacing(val), + Reveal::All => ConstDedupResult::All(val), + } + } +} + +/// Used to store results of calls to `eval_to_allocation_raw` and +/// `eval_to_const_value_raw`. +#[derive(Debug)] +pub struct ConstDedupMap<'tcx> { + // interning for deduplication of `eval_to_allocation_raw` + pub alloc_map: RefCell, ConstDedupResult>>>, + + // interning for deduplication of `eval_to_const_value_raw` + pub const_val_map: RefCell, ConstDedupResult>>>, +} + +impl<'tcx> ConstDedupMap<'tcx> { + pub fn new() -> Self { + ConstDedupMap { alloc_map: Default::default(), const_val_map: Default::default() } + } + + #[instrument(skip(self), level = "debug")] + fn insert_alloc(&self, id: GlobalId<'tcx>, val: ConstDedupResult>) { + let mut alloc_map = self.alloc_map.borrow_mut(); + alloc_map.insert(id, val); + debug!("alloc_map after update: {:#?}", alloc_map); + } + + #[instrument(skip(self), level = "debug")] + fn insert_const_val(&self, id: GlobalId<'tcx>, val: ConstDedupResult>) { + let mut const_val_map = self.const_val_map.borrow_mut(); + const_val_map.insert(id, val); + debug!("const_val_map after update: {:#?}", const_val_map); + } +} + impl<'tcx> TyCtxt<'tcx> { /// Obtains a new allocation ID that can be referenced but does not /// yet have an allocation backing it. @@ -554,6 +641,129 @@ impl<'tcx> TyCtxt<'tcx> { fn set_alloc_id_same_memory(self, id: AllocId, mem: &'tcx Allocation) { self.alloc_map.lock().alloc_map.insert_same(id, GlobalAlloc::Memory(mem)); } + + /// Store the result of a call to `eval_to_allocation_raw` in order to + /// allow deduplication. + #[instrument(skip(self), level = "debug")] + pub fn save_alloc_for_dedup(self, id: GlobalId<'tcx>, val: ConstDedupResult>) { + let dedup_const_map = self.dedup_const_map.lock(); + dedup_const_map.insert_alloc(id, val); + debug!("dedup_const_map after insert: {:#?}", dedup_const_map); + } + + /// Store the result of a call to `eval_to_const_value_raw` in order to deduplicate it. + #[instrument(skip(self), level = "debug")] + pub fn save_const_value_for_dedup( + self, + id: GlobalId<'tcx>, + val: ConstDedupResult>, + ) { + let dedup_const_map = self.dedup_const_map.lock(); + dedup_const_map.insert_const_val(id, val); + debug!("dedup_const_map after insert: {:#?}", dedup_const_map); + } + + /// Tries to deduplicate a call to `eval_to_allocation_raw`. If deduplication isn't + /// successful `eval_to_allocation_raw` query is executed. + #[instrument(skip(self, opt_span), level = "debug")] + pub fn dedup_eval_alloc_raw( + self, + key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, + opt_span: Option, + ) -> EvalToAllocationRawResult<'tcx> { + use ConstDedupResult::*; + + let (param_env, id) = key.into_parts(); + let dedup_const_map = self.dedup_const_map.lock(); + debug!("dedup_const_map: {:#?}", dedup_const_map); + let alloc_map = dedup_const_map.alloc_map.borrow(); + debug!("alloc_map: {:#?}", alloc_map); + + let dedup_result = alloc_map.get(&id); + debug!(?dedup_result); + + match param_env.reveal() { + Reveal::Selection => match dedup_result { + Some(Selection(alloc) | UserFacing(alloc)) => return Ok(*alloc), + _ => {} + }, + Reveal::UserFacing => match dedup_result { + Some(Selection(alloc) | UserFacing(alloc)) => { + return Ok(*alloc); + } + _ => {} + }, + Reveal::All => match dedup_result { + Some(Selection(alloc) | UserFacing(alloc) | All(alloc)) => { + return Ok(*alloc); + } + _ => {} + }, + } + + // Important to drop the lock here + drop(alloc_map); + drop(dedup_const_map); + + debug!("unable to deduplicate"); + + // We weren't able to deduplicate + match opt_span { + Some(span) => self.at(span).eval_to_allocation_raw(key), + None => self.eval_to_allocation_raw(key), + } + } + + /// Tries to deduplicate a call to `eval_to_const_value_raw`. If deduplication isn't + /// successful, `eval_to_const_value_raw` query is executed. + #[instrument(skip(self), level = "debug")] + pub fn dedup_eval_const_value_raw( + self, + key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, + opt_span: Option, + ) -> EvalToConstValueResult<'tcx> { + use ConstDedupResult::*; + + let (param_env, id) = key.into_parts(); + let dedup_const_map = self.dedup_const_map.lock(); + debug!("dedup_const_map: {:#?}", dedup_const_map); + let const_val_map = dedup_const_map.const_val_map.borrow(); + debug!("const_val_map: {:#?}", const_val_map); + + let dedup_result = const_val_map.get(&id); + debug!(?dedup_result); + + match param_env.reveal() { + Reveal::Selection => match dedup_result { + Some(Selection(const_val) | UserFacing(const_val)) => return Ok(*const_val), + _ => {} + }, + Reveal::UserFacing => match dedup_result { + Some(Selection(const_value) | UserFacing(const_value)) => { + return Ok(*const_value); + } + _ => {} + }, + Reveal::All => match dedup_result { + Some(Selection(const_value) | UserFacing(const_value) | All(const_value)) => { + return Ok(*const_value); + } + _ => {} + }, + } + + // Important to drop the lock here + drop(const_val_map); + drop(dedup_const_map); + + debug!("unable to deduplicate"); + + // We weren't able to deduplicate + match opt_span { + Some(span) => self.at(span).eval_to_const_value_raw(key), + None => self.eval_to_const_value_raw(key), + } + } } //////////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index d6204a8efb783..cac85042ad5ef 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -3,6 +3,7 @@ use std::fmt; use crate::mir::interpret::{AllocId, ConstValue, Scalar}; use crate::mir::Promoted; +use crate::traits::Reveal; use crate::ty::subst::{InternalSubsts, SubstsRef}; use crate::ty::ParamEnv; use crate::ty::{self, TyCtxt, TypeFoldable}; @@ -160,10 +161,15 @@ impl<'tcx> ConstKind<'tcx> { // Note that we erase regions *before* calling `with_reveal_all_normalized`, // so that we don't try to invoke this query with // any region variables. - let param_env_and = tcx - .erase_regions(param_env) - .with_reveal_all_normalized(tcx) - .and(tcx.erase_regions(unevaluated)); + let param_env_and = match param_env.reveal() { + Reveal::Selection => { + tcx.erase_regions(param_env).and(tcx.erase_regions(unevaluated)) + } + _ => tcx + .erase_regions(param_env) + .with_reveal_all_normalized(tcx) + .and(tcx.erase_regions(unevaluated)), + }; // HACK(eddyb) when the query key would contain inference variables, // attempt using identity substs and `ParamEnv` instead, that will succeed diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index caa7008f1085e..6086117b8111f 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1043,6 +1043,10 @@ pub struct GlobalCtxt<'tcx> { /// Stores memory for globals (statics/consts). pub(crate) alloc_map: Lock>, + /// Used to deduplicate calls to `eval_to_allocation_raw` and + /// `eval_to_const_value_raw`. + pub dedup_const_map: Lock>, + output_filenames: Arc, } @@ -1187,6 +1191,7 @@ impl<'tcx> TyCtxt<'tcx> { stability_interner: Default::default(), const_stability_interner: Default::default(), alloc_map: Lock::new(interpret::AllocMap::new()), + dedup_const_map: Lock::new(interpret::ConstDedupMap::new()), output_filenames: Arc::new(output_filenames), } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 72514e4456105..a6558e2af2a48 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -150,11 +150,7 @@ pub struct MainDefinition { impl MainDefinition { pub fn opt_fn_def_id(self) -> Option { - if let Res::Def(DefKind::Fn, def_id) = self.res { - Some(def_id) - } else { - None - } + if let Res::Def(DefKind::Fn, def_id) = self.res { Some(def_id) } else { None } } } @@ -1184,11 +1180,7 @@ impl WithOptConstParam { } pub fn def_id_for_type_of(self) -> DefId { - if let Some(did) = self.const_param_did { - did - } else { - self.did.to_def_id() - } + if let Some(did) = self.const_param_did { did } else { self.did.to_def_id() } } } @@ -1245,23 +1237,28 @@ struct ParamTag { } unsafe impl rustc_data_structures::tagged_ptr::Tag for ParamTag { - const BITS: usize = 2; + const BITS: usize = 3; #[inline] fn into_usize(self) -> usize { match self { - Self { reveal: traits::Reveal::UserFacing, constness: hir::Constness::NotConst } => 0, - Self { reveal: traits::Reveal::All, constness: hir::Constness::NotConst } => 1, - Self { reveal: traits::Reveal::UserFacing, constness: hir::Constness::Const } => 2, - Self { reveal: traits::Reveal::All, constness: hir::Constness::Const } => 3, + Self { reveal: traits::Reveal::Selection, constness: hir::Constness::NotConst } => 0, + Self { reveal: traits::Reveal::UserFacing, constness: hir::Constness::NotConst } => 1, + Self { reveal: traits::Reveal::All, constness: hir::Constness::NotConst } => 2, + Self { reveal: traits::Reveal::Selection, constness: hir::Constness::Const } => 3, + Self { reveal: traits::Reveal::UserFacing, constness: hir::Constness::Const } => 4, + Self { reveal: traits::Reveal::All, constness: hir::Constness::Const } => 5, } } + #[inline] unsafe fn from_usize(ptr: usize) -> Self { match ptr { - 0 => Self { reveal: traits::Reveal::UserFacing, constness: hir::Constness::NotConst }, - 1 => Self { reveal: traits::Reveal::All, constness: hir::Constness::NotConst }, - 2 => Self { reveal: traits::Reveal::UserFacing, constness: hir::Constness::Const }, - 3 => Self { reveal: traits::Reveal::All, constness: hir::Constness::Const }, + 0 => Self { reveal: traits::Reveal::Selection, constness: hir::Constness::NotConst }, + 1 => Self { reveal: traits::Reveal::UserFacing, constness: hir::Constness::NotConst }, + 2 => Self { reveal: traits::Reveal::All, constness: hir::Constness::NotConst }, + 3 => Self { reveal: traits::Reveal::Selection, constness: hir::Constness::Const }, + 4 => Self { reveal: traits::Reveal::UserFacing, constness: hir::Constness::Const }, + 5 => Self { reveal: traits::Reveal::All, constness: hir::Constness::Const }, _ => std::hint::unreachable_unchecked(), } } @@ -1352,7 +1349,7 @@ impl<'tcx> ParamEnv<'tcx> { } pub fn with_reveal_selection(mut self) -> Self { - self.packed.set_tag(Reveal::Selection); + self.packed.set_tag(ParamTag { reveal: Reveal::Selection, ..self.packed.tag() }); self } diff --git a/compiler/rustc_mir/src/const_eval/eval_queries.rs b/compiler/rustc_mir/src/const_eval/eval_queries.rs deleted file mode 100644 index 303ebc829dc9f..0000000000000 --- a/compiler/rustc_mir/src/const_eval/eval_queries.rs +++ /dev/null @@ -1,453 +0,0 @@ -use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr, MemoryExtra}; -use crate::interpret::eval_nullary_intrinsic; -use crate::interpret::{ - intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId, - Immediate, InternKind, InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, Scalar, - ScalarMaybeUninit, StackPopCleanup, -}; - -use rustc_errors::ErrorReported; -use rustc_hir::def::DefKind; -use rustc_middle::mir; -use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::traits::Reveal; -use rustc_middle::ty::layout::LayoutError; -use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, subst::Subst, TyCtxt}; -use rustc_span::source_map::Span; -use rustc_target::abi::{Abi, LayoutOf}; -use std::convert::TryInto; - -pub fn note_on_undefined_behavior_error() -> &'static str { - "The rules on what exactly is undefined behavior aren't clear, \ - so this check might be overzealous. Please open an issue on the rustc \ - repository if you believe it should not be considered undefined behavior." -} - -// Returns a pointer to where the result lives -fn eval_body_using_ecx<'mir, 'tcx>( - ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, - cid: GlobalId<'tcx>, - body: &'mir mir::Body<'tcx>, -) -> InterpResult<'tcx, MPlaceTy<'tcx>> { - debug!("eval_body_using_ecx: {:?}, {:?}", cid, ecx.param_env); - let tcx = *ecx.tcx; - assert!( - cid.promoted.is_some() - || matches!( - ecx.tcx.def_kind(cid.instance.def_id()), - DefKind::Const - | DefKind::Static - | DefKind::ConstParam - | DefKind::AnonConst - | DefKind::AssocConst - ), - "Unexpected DefKind: {:?}", - ecx.tcx.def_kind(cid.instance.def_id()) - ); - let layout = ecx.layout_of(body.return_ty().subst(tcx, cid.instance.substs))?; - assert!(!layout.is_unsized()); - let ret = ecx.allocate(layout, MemoryKind::Stack); - - let name = - with_no_trimmed_paths(|| ty::tls::with(|tcx| tcx.def_path_str(cid.instance.def_id()))); - let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p)); - trace!("eval_body_using_ecx: pushing stack frame for global: {}{}", name, prom); - - ecx.push_stack_frame( - cid.instance, - body, - Some(ret.into()), - StackPopCleanup::None { cleanup: false }, - )?; - - // The main interpreter loop. - ecx.run()?; - - // Intern the result - let intern_kind = if cid.promoted.is_some() { - InternKind::Promoted - } else { - match tcx.static_mutability(cid.instance.def_id()) { - Some(m) => InternKind::Static(m), - None => InternKind::Constant, - } - }; - intern_const_alloc_recursive(ecx, intern_kind, ret)?; - - debug!("eval_body_using_ecx done: {:?}", *ret); - Ok(ret) -} - -/// The `InterpCx` is only meant to be used to do field and index projections into constants for -/// `simd_shuffle` and const patterns in match arms. -/// -/// The function containing the `match` that is currently being analyzed may have generic bounds -/// that inform us about the generic bounds of the constant. E.g., using an associated constant -/// of a function's generic parameter will require knowledge about the bounds on the generic -/// parameter. These bounds are passed to `mk_eval_cx` via the `ParamEnv` argument. -pub(super) fn mk_eval_cx<'mir, 'tcx>( - tcx: TyCtxt<'tcx>, - root_span: Span, - param_env: ty::ParamEnv<'tcx>, - can_access_statics: bool, -) -> CompileTimeEvalContext<'mir, 'tcx> { - debug!("mk_eval_cx: {:?}", param_env); - InterpCx::new( - tcx, - root_span, - param_env, - CompileTimeInterpreter::new(tcx.sess.const_eval_limit()), - MemoryExtra { can_access_statics }, - ) -} - -/// This function converts an interpreter value into a constant that is meant for use in the -/// type system. -pub(super) fn op_to_const<'tcx>( - ecx: &CompileTimeEvalContext<'_, 'tcx>, - op: OpTy<'tcx>, -) -> ConstValue<'tcx> { - // We do not have value optimizations for everything. - // Only scalars and slices, since they are very common. - // Note that further down we turn scalars of uninitialized bits back to `ByRef`. These can result - // from scalar unions that are initialized with one of their zero sized variants. We could - // instead allow `ConstValue::Scalar` to store `ScalarMaybeUninit`, but that would affect all - // the usual cases of extracting e.g. a `usize`, without there being a real use case for the - // `Undef` situation. - let try_as_immediate = match op.layout.abi { - Abi::Scalar(..) => true, - Abi::ScalarPair(..) => match op.layout.ty.kind() { - ty::Ref(_, inner, _) => match *inner.kind() { - ty::Slice(elem) => elem == ecx.tcx.types.u8, - ty::Str => true, - _ => false, - }, - _ => false, - }, - _ => false, - }; - let immediate = if try_as_immediate { - Err(ecx.read_immediate(op).expect("normalization works on validated constants")) - } else { - // It is guaranteed that any non-slice scalar pair is actually ByRef here. - // When we come back from raw const eval, we are always by-ref. The only way our op here is - // by-val is if we are in destructure_const, i.e., if this is (a field of) something that we - // "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or - // structs containing such. - op.try_as_mplace(ecx) - }; - - let to_const_value = |mplace: MPlaceTy<'_>| match mplace.ptr { - Scalar::Ptr(ptr) => { - let alloc = ecx.tcx.global_alloc(ptr.alloc_id).unwrap_memory(); - ConstValue::ByRef { alloc, offset: ptr.offset } - } - Scalar::Int(int) => { - assert!(mplace.layout.is_zst()); - assert_eq!( - int.assert_bits(ecx.tcx.data_layout.pointer_size) - % u128::from(mplace.layout.align.abi.bytes()), - 0, - "this MPlaceTy must come from a validated constant, thus we can assume the \ - alignment is correct", - ); - ConstValue::Scalar(Scalar::ZST) - } - }; - match immediate { - Ok(mplace) => to_const_value(mplace), - // see comment on `let try_as_immediate` above - Err(imm) => match *imm { - Immediate::Scalar(x) => match x { - ScalarMaybeUninit::Scalar(s) => ConstValue::Scalar(s), - ScalarMaybeUninit::Uninit => to_const_value(op.assert_mem_place(ecx)), - }, - Immediate::ScalarPair(a, b) => { - let (data, start) = match a.check_init().unwrap() { - Scalar::Ptr(ptr) => { - (ecx.tcx.global_alloc(ptr.alloc_id).unwrap_memory(), ptr.offset.bytes()) - } - Scalar::Int { .. } => ( - ecx.tcx - .intern_const_alloc(Allocation::from_byte_aligned_bytes(b"" as &[u8])), - 0, - ), - }; - let len = b.to_machine_usize(ecx).unwrap(); - let start = start.try_into().unwrap(); - let len: usize = len.try_into().unwrap(); - ConstValue::Slice { data, start, end: start + len } - } - }, - } -} - -fn turn_into_const_value<'tcx>( - tcx: TyCtxt<'tcx>, - constant: ConstAlloc<'tcx>, - key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, -) -> ConstValue<'tcx> { - let cid = key.value; - let def_id = cid.instance.def.def_id(); - let is_static = tcx.is_static(def_id); - let ecx = mk_eval_cx(tcx, tcx.def_span(key.value.instance.def_id()), key.param_env, is_static); - - let mplace = ecx.raw_const_to_mplace(constant).expect( - "can only fail if layout computation failed, \ - which should have given a good error before ever invoking this function", - ); - assert!( - !is_static || cid.promoted.is_some(), - "the `eval_to_const_value_raw` query should not be used for statics, use `eval_to_allocation` instead" - ); - // Turn this into a proper constant. - op_to_const(&ecx, mplace.into()) -} - -pub fn eval_to_const_value_raw_provider<'tcx>( - tcx: TyCtxt<'tcx>, - key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, -) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> { - // see comment in const_eval_raw_provider for what we're doing here - match key.param_env.reveal() { - Reveal::Selection => {} - Reveal::UserFacing => { - let mut key = key; - key.param_env = key.param_env.with_reveal_selection(); - match tcx.eval_to_const_value_raw(key) { - // try again with reveal all as requested - Err(ErrorHandled::Silent) => {} - // deduplicate calls - other => return other, - } - } - Reveal::All => { - let mut key = key; - key.param_env = key.param_env.with_user_facing(); - match tcx.eval_to_const_value_raw(key) { - // try again with reveal all as requested - Err(ErrorHandled::Silent) => bug!("unexpected error for {:?}", key), - Err(ErrorHandled::TooGeneric) => {} - // deduplicate calls - other => return other, - } - } - } - - // We call `const_eval` for zero arg intrinsics, too, in order to cache their value. - // Catch such calls and evaluate them instead of trying to load a constant's MIR. - if let ty::InstanceDef::Intrinsic(def_id) = key.value.instance.def { - let ty = key.value.instance.ty(tcx, key.param_env); - let substs = match ty.kind() { - ty::FnDef(_, substs) => substs, - _ => bug!("intrinsic with type {:?}", ty), - }; - return eval_nullary_intrinsic(tcx, key.param_env, def_id, substs).map_err(|error| { - let span = tcx.def_span(def_id); - let error = ConstEvalErr { error: error.kind, stacktrace: vec![], span }; - error.report_as_error(tcx.at(span), "could not evaluate nullary intrinsic") - }); - } - - tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key)) -} - -pub fn eval_to_allocation_raw_provider<'tcx>( - tcx: TyCtxt<'tcx>, - key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, -) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> { - // Because the constant is computed twice (once per value of `Reveal`), we are at risk of - // reporting the same error twice here. To resolve this, we check whether we can evaluate the - // constant in the more restrictive `Reveal::UserFacing`, which most likely already was - // computed. For a large percentage of constants that will already have succeeded. Only - // associated constants of generic functions will fail due to not enough monomorphization - // information being available. - match key.param_env.reveal() { - Reveal::Selection => {} - Reveal::UserFacing => { - let mut key = key; - key.param_env = key.param_env.with_reveal_selection(); - match tcx.eval_to_allocation_raw(key) { - Err(ErrorHandled::Silent) => {} - // deduplicate calls - other => return other, - } - } - Reveal::All => { - let mut key = key; - key.param_env = key.param_env.with_user_facing(); - match tcx.eval_to_allocation_raw(key) { - Err(ErrorHandled::Silent) => bug!("unexpected error for {:?}", key), - Err(ErrorHandled::TooGeneric) => {} - // deduplicate calls - other => return other, - } - } - } - - if cfg!(debug_assertions) { - // Make sure we format the instance even if we do not print it. - // This serves as a regression test against an ICE on printing. - // The next two lines concatenated contain some discussion: - // https://rust-lang.zulipchat.com/#narrow/stream/146212-t-compiler.2Fconst-eval/ - // subject/anon_const_instance_printing/near/135980032 - let instance = with_no_trimmed_paths(|| key.value.instance.to_string()); - trace!("const eval: {:?} ({})", key, instance); - } - - let cid = key.value; - let def = cid.instance.def.with_opt_param(); - - if let Some(def) = def.as_local() { - if tcx.has_typeck_results(def.did) { - if let Some(error_reported) = tcx.typeck_opt_const_arg(def).tainted_by_errors { - return Err(ErrorHandled::Reported(error_reported)); - } - } - if !tcx.is_mir_available(def.did) { - tcx.sess.delay_span_bug( - tcx.def_span(def.did), - &format!("no MIR body is available for {:?}", def.did), - ); - return Err(ErrorHandled::Reported(ErrorReported {})); - } - if let Some(error_reported) = tcx.mir_const_qualif_opt_const_arg(def).error_occured { - return Err(ErrorHandled::Reported(error_reported)); - } - } - - let is_static = tcx.is_static(def.did); - - let mut ecx = InterpCx::new( - tcx, - tcx.def_span(def.did), - key.param_env, - CompileTimeInterpreter::new(tcx.sess.const_eval_limit()), - MemoryExtra { can_access_statics: is_static }, - ); - - let res = ecx.load_mir(cid.instance.def, cid.promoted); - match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) { - Err(error) => { - if key.param_env.reveal() == Reveal::Selection { - match error.kind { - err_inval!(Layout(LayoutError::Unknown(_))) - | err_inval!(TooGeneric) - | err_inval!(AlreadyReported(_)) => {} - _ => return Err(ErrorHandled::Silent), - } - } - - let err = ConstEvalErr::new(&ecx, error, None); - // errors in statics are always emitted as fatal errors - if is_static { - // Ensure that if the above error was either `TooGeneric` or `Reported` - // an error must be reported. - let v = err.report_as_error( - ecx.tcx.at(ecx.cur_span()), - "could not evaluate static initializer", - ); - - // If this is `Reveal:All`, then we need to make sure an error is reported but if - // this is `Reveal::UserFacing`, then it's expected that we could get a - // `TooGeneric` error. When we fall back to `Reveal::All`, then it will either - // succeed or we'll report this error then. - if key.param_env.reveal() == Reveal::All { - tcx.sess.delay_span_bug( - err.span, - &format!("static eval failure did not emit an error: {:#?}", v), - ); - } - - Err(v) - } else if let Some(def) = def.as_local() { - // constant defined in this crate, we can figure out a lint level! - match tcx.def_kind(def.did.to_def_id()) { - // constants never produce a hard error at the definition site. Anything else is - // a backwards compatibility hazard (and will break old versions of winapi for - // sure) - // - // note that validation may still cause a hard error on this very same constant, - // because any code that existed before validation could not have failed - // validation thus preventing such a hard error from being a backwards - // compatibility hazard - DefKind::Const | DefKind::AssocConst => { - let hir_id = tcx.hir().local_def_id_to_hir_id(def.did); - Err(err.report_as_lint( - tcx.at(tcx.def_span(def.did)), - "any use of this value will cause an error", - hir_id, - Some(err.span), - )) - } - // promoting runtime code is only allowed to error if it references broken - // constants any other kind of error will be reported to the user as a - // deny-by-default lint - _ => { - if let Some(p) = cid.promoted { - let span = tcx.promoted_mir_opt_const_arg(def.to_global())[p].span; - if let err_inval!(ReferencedConstant) = err.error { - Err(err.report_as_error( - tcx.at(span), - "evaluation of constant expression failed", - )) - } else { - Err(err.report_as_lint( - tcx.at(span), - "reaching this expression at runtime will panic or abort", - tcx.hir().local_def_id_to_hir_id(def.did), - Some(err.span), - )) - } - // anything else (array lengths, enum initializers, constant patterns) are - // reported as hard errors - } else { - Err(err.report_as_error( - ecx.tcx.at(ecx.cur_span()), - "evaluation of constant value failed", - )) - } - } - } - } else { - // use of broken constant from other crate - Err(err.report_as_error(ecx.tcx.at(ecx.cur_span()), "could not evaluate constant")) - } - } - Ok(mplace) => { - // Since evaluation had no errors, valiate the resulting constant: - let validation = try { - let mut ref_tracking = RefTracking::new(mplace); - let mut inner = false; - while let Some((mplace, path)) = ref_tracking.todo.pop() { - let mode = match tcx.static_mutability(cid.instance.def_id()) { - Some(_) if cid.promoted.is_some() => { - // Promoteds in statics are allowed to point to statics. - CtfeValidationMode::Const { inner, allow_static_ptrs: true } - } - Some(_) => CtfeValidationMode::Regular, // a `static` - None => CtfeValidationMode::Const { inner, allow_static_ptrs: false }, - }; - ecx.const_validate_operand(mplace.into(), path, &mut ref_tracking, mode)?; - inner = true; - } - }; - if let Err(error) = validation { - // Validation failed, report an error - let err = ConstEvalErr::new(&ecx, error, None); - Err(err.struct_error( - ecx.tcx, - "it is undefined behavior to use this value", - |mut diag| { - diag.note(note_on_undefined_behavior_error()); - diag.emit(); - }, - )) - } else { - // Convert to raw constant - Ok(ConstAlloc { alloc_id: mplace.ptr.assert_ptr().alloc_id, ty: mplace.layout.ty }) - } - } - } -} diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs deleted file mode 100644 index a2a58207f279f..0000000000000 --- a/compiler/rustc_mir/src/monomorphize/collector.rs +++ /dev/null @@ -1,1265 +0,0 @@ -//! Mono Item Collection -//! ==================== -//! -//! This module is responsible for discovering all items that will contribute -//! to code generation of the crate. The important part here is that it not only -//! needs to find syntax-level items (functions, structs, etc) but also all -//! their monomorphized instantiations. Every non-generic, non-const function -//! maps to one LLVM artifact. Every generic function can produce -//! from zero to N artifacts, depending on the sets of type arguments it -//! is instantiated with. -//! This also applies to generic items from other crates: A generic definition -//! in crate X might produce monomorphizations that are compiled into crate Y. -//! We also have to collect these here. -//! -//! The following kinds of "mono items" are handled here: -//! -//! - Functions -//! - Methods -//! - Closures -//! - Statics -//! - Drop glue -//! -//! The following things also result in LLVM artifacts, but are not collected -//! here, since we instantiate them locally on demand when needed in a given -//! codegen unit: -//! -//! - Constants -//! - Vtables -//! - Object Shims -//! -//! -//! General Algorithm -//! ----------------- -//! Let's define some terms first: -//! -//! - A "mono item" is something that results in a function or global in -//! the LLVM IR of a codegen unit. Mono items do not stand on their -//! own, they can reference other mono items. For example, if function -//! `foo()` calls function `bar()` then the mono item for `foo()` -//! references the mono item for function `bar()`. In general, the -//! definition for mono item A referencing a mono item B is that -//! the LLVM artifact produced for A references the LLVM artifact produced -//! for B. -//! -//! - Mono items and the references between them form a directed graph, -//! where the mono items are the nodes and references form the edges. -//! Let's call this graph the "mono item graph". -//! -//! - The mono item graph for a program contains all mono items -//! that are needed in order to produce the complete LLVM IR of the program. -//! -//! The purpose of the algorithm implemented in this module is to build the -//! mono item graph for the current crate. It runs in two phases: -//! -//! 1. Discover the roots of the graph by traversing the HIR of the crate. -//! 2. Starting from the roots, find neighboring nodes by inspecting the MIR -//! representation of the item corresponding to a given node, until no more -//! new nodes are found. -//! -//! ### Discovering roots -//! -//! The roots of the mono item graph correspond to the non-generic -//! syntactic items in the source code. We find them by walking the HIR of the -//! crate, and whenever we hit upon a function, method, or static item, we -//! create a mono item consisting of the items DefId and, since we only -//! consider non-generic items, an empty type-substitution set. -//! -//! ### Finding neighbor nodes -//! Given a mono item node, we can discover neighbors by inspecting its -//! MIR. We walk the MIR and any time we hit upon something that signifies a -//! reference to another mono item, we have found a neighbor. Since the -//! mono item we are currently at is always monomorphic, we also know the -//! concrete type arguments of its neighbors, and so all neighbors again will be -//! monomorphic. The specific forms a reference to a neighboring node can take -//! in MIR are quite diverse. Here is an overview: -//! -//! #### Calling Functions/Methods -//! The most obvious form of one mono item referencing another is a -//! function or method call (represented by a CALL terminator in MIR). But -//! calls are not the only thing that might introduce a reference between two -//! function mono items, and as we will see below, they are just a -//! specialization of the form described next, and consequently will not get any -//! special treatment in the algorithm. -//! -//! #### Taking a reference to a function or method -//! A function does not need to actually be called in order to be a neighbor of -//! another function. It suffices to just take a reference in order to introduce -//! an edge. Consider the following example: -//! -//! ```rust -//! fn print_val(x: T) { -//! println!("{}", x); -//! } -//! -//! fn call_fn(f: &Fn(i32), x: i32) { -//! f(x); -//! } -//! -//! fn main() { -//! let print_i32 = print_val::; -//! call_fn(&print_i32, 0); -//! } -//! ``` -//! The MIR of none of these functions will contain an explicit call to -//! `print_val::`. Nonetheless, in order to mono this program, we need -//! an instance of this function. Thus, whenever we encounter a function or -//! method in operand position, we treat it as a neighbor of the current -//! mono item. Calls are just a special case of that. -//! -//! #### Closures -//! In a way, closures are a simple case. Since every closure object needs to be -//! constructed somewhere, we can reliably discover them by observing -//! `RValue::Aggregate` expressions with `AggregateKind::Closure`. This is also -//! true for closures inlined from other crates. -//! -//! #### Drop glue -//! Drop glue mono items are introduced by MIR drop-statements. The -//! generated mono item will again have drop-glue item neighbors if the -//! type to be dropped contains nested values that also need to be dropped. It -//! might also have a function item neighbor for the explicit `Drop::drop` -//! implementation of its type. -//! -//! #### Unsizing Casts -//! A subtle way of introducing neighbor edges is by casting to a trait object. -//! Since the resulting fat-pointer contains a reference to a vtable, we need to -//! instantiate all object-save methods of the trait, as we need to store -//! pointers to these functions even if they never get called anywhere. This can -//! be seen as a special case of taking a function reference. -//! -//! #### Boxes -//! Since `Box` expression have special compiler support, no explicit calls to -//! `exchange_malloc()` and `box_free()` may show up in MIR, even if the -//! compiler will generate them. We have to observe `Rvalue::Box` expressions -//! and Box-typed drop-statements for that purpose. -//! -//! -//! Interaction with Cross-Crate Inlining -//! ------------------------------------- -//! The binary of a crate will not only contain machine code for the items -//! defined in the source code of that crate. It will also contain monomorphic -//! instantiations of any extern generic functions and of functions marked with -//! `#[inline]`. -//! The collection algorithm handles this more or less mono. If it is -//! about to create a mono item for something with an external `DefId`, -//! it will take a look if the MIR for that item is available, and if so just -//! proceed normally. If the MIR is not available, it assumes that the item is -//! just linked to and no node is created; which is exactly what we want, since -//! no machine code should be generated in the current crate for such an item. -//! -//! Eager and Lazy Collection Mode -//! ------------------------------ -//! Mono item collection can be performed in one of two modes: -//! -//! - Lazy mode means that items will only be instantiated when actually -//! referenced. The goal is to produce the least amount of machine code -//! possible. -//! -//! - Eager mode is meant to be used in conjunction with incremental compilation -//! where a stable set of mono items is more important than a minimal -//! one. Thus, eager mode will instantiate drop-glue for every drop-able type -//! in the crate, even if no drop call for that type exists (yet). It will -//! also instantiate default implementations of trait methods, something that -//! otherwise is only done on demand. -//! -//! -//! Open Issues -//! ----------- -//! Some things are not yet fully implemented in the current version of this -//! module. -//! -//! ### Const Fns -//! Ideally, no mono item should be generated for const fns unless there -//! is a call to them that cannot be evaluated at compile time. At the moment -//! this is not implemented however: a mono item will be produced -//! regardless of whether it is actually needed or not. - -use crate::monomorphize; - -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::sync::{par_iter, MTLock, MTRef, ParallelIterator}; -use rustc_errors::{ErrorReported, FatalError}; -use rustc_hir as hir; -use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; -use rustc_hir::itemlikevisit::ItemLikeVisitor; -use rustc_hir::lang_items::LangItem; -use rustc_index::bit_set::GrowableBitSet; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::mir::interpret::{AllocId, ConstValue}; -use rustc_middle::mir::interpret::{ErrorHandled, GlobalAlloc, Scalar}; -use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; -use rustc_middle::mir::visit::Visitor as MirVisitor; -use rustc_middle::mir::{self, Local, Location}; -use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCast}; -use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; -use rustc_middle::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable}; -use rustc_session::config::EntryFnType; -use rustc_span::source_map::{dummy_spanned, respan, Span, Spanned, DUMMY_SP}; -use smallvec::SmallVec; -use std::iter; -use std::ops::Range; -use std::path::PathBuf; - -#[derive(PartialEq)] -pub enum MonoItemCollectionMode { - Eager, - Lazy, -} - -/// Maps every mono item to all mono items it references in its -/// body. -pub struct InliningMap<'tcx> { - // Maps a source mono item to the range of mono items - // accessed by it. - // The range selects elements within the `targets` vecs. - index: FxHashMap, Range>, - targets: Vec>, - - // Contains one bit per mono item in the `targets` field. That bit - // is true if that mono item needs to be inlined into every CGU. - inlines: GrowableBitSet, -} - -impl<'tcx> InliningMap<'tcx> { - fn new() -> InliningMap<'tcx> { - InliningMap { - index: FxHashMap::default(), - targets: Vec::new(), - inlines: GrowableBitSet::with_capacity(1024), - } - } - - fn record_accesses(&mut self, source: MonoItem<'tcx>, new_targets: &[(MonoItem<'tcx>, bool)]) { - let start_index = self.targets.len(); - let new_items_count = new_targets.len(); - let new_items_count_total = new_items_count + self.targets.len(); - - self.targets.reserve(new_items_count); - self.inlines.ensure(new_items_count_total); - - for (i, (target, inline)) in new_targets.iter().enumerate() { - self.targets.push(*target); - if *inline { - self.inlines.insert(i + start_index); - } - } - - let end_index = self.targets.len(); - assert!(self.index.insert(source, start_index..end_index).is_none()); - } - - // Internally iterate over all items referenced by `source` which will be - // made available for inlining. - pub fn with_inlining_candidates(&self, source: MonoItem<'tcx>, mut f: F) - where - F: FnMut(MonoItem<'tcx>), - { - if let Some(range) = self.index.get(&source) { - for (i, candidate) in self.targets[range.clone()].iter().enumerate() { - if self.inlines.contains(range.start + i) { - f(*candidate); - } - } - } - } - - // Internally iterate over all items and the things each accesses. - pub fn iter_accesses(&self, mut f: F) - where - F: FnMut(MonoItem<'tcx>, &[MonoItem<'tcx>]), - { - for (&accessor, range) in &self.index { - f(accessor, &self.targets[range.clone()]) - } - } -} - -pub fn collect_crate_mono_items( - tcx: TyCtxt<'_>, - mode: MonoItemCollectionMode, -) -> (FxHashSet>, InliningMap<'_>) { - let _prof_timer = tcx.prof.generic_activity("monomorphization_collector"); - - let roots = - tcx.sess.time("monomorphization_collector_root_collections", || collect_roots(tcx, mode)); - - debug!("building mono item graph, beginning at roots"); - - let mut visited = MTLock::new(FxHashSet::default()); - let mut inlining_map = MTLock::new(InliningMap::new()); - - { - let visited: MTRef<'_, _> = &mut visited; - let inlining_map: MTRef<'_, _> = &mut inlining_map; - - tcx.sess.time("monomorphization_collector_graph_walk", || { - par_iter(roots).for_each(|root| { - let mut recursion_depths = DefIdMap::default(); - collect_items_rec( - tcx, - dummy_spanned(root), - visited, - &mut recursion_depths, - inlining_map, - ); - }); - }); - } - - (visited.into_inner(), inlining_map.into_inner()) -} - -// Find all non-generic items by walking the HIR. These items serve as roots to -// start monomorphizing from. -fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionMode) -> Vec> { - debug!("collecting roots"); - let mut roots = Vec::new(); - - { - let entry_fn = tcx.entry_fn(LOCAL_CRATE); - - debug!("collect_roots: entry_fn = {:?}", entry_fn); - - let mut visitor = RootCollector { tcx, mode, entry_fn, output: &mut roots }; - - tcx.hir().krate().visit_all_item_likes(&mut visitor); - - visitor.push_extra_entry_roots(); - } - - // We can only codegen items that are instantiable - items all of - // whose predicates hold. Luckily, items that aren't instantiable - // can't actually be used, so we can just skip codegenning them. - roots - .into_iter() - .filter_map(|root| root.node.is_instantiable(tcx).then_some(root.node)) - .collect() -} - -// Collect all monomorphized items reachable from `starting_point` -fn collect_items_rec<'tcx>( - tcx: TyCtxt<'tcx>, - starting_point: Spanned>, - visited: MTRef<'_, MTLock>>>, - recursion_depths: &mut DefIdMap, - inlining_map: MTRef<'_, MTLock>>, -) { - if !visited.lock_mut().insert(starting_point.node) { - // We've been here already, no need to search again. - return; - } - debug!("BEGIN collect_items_rec({})", starting_point.node); - - let mut neighbors = Vec::new(); - let recursion_depth_reset; - - match starting_point.node { - MonoItem::Static(def_id) => { - let instance = Instance::mono(tcx, def_id); - - // Sanity check whether this ended up being collected accidentally - debug_assert!(should_codegen_locally(tcx, &instance)); - - let ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); - visit_drop_use(tcx, ty, true, starting_point.span, &mut neighbors); - - recursion_depth_reset = None; - - if let Ok(alloc) = tcx.eval_static_initializer(def_id) { - for &((), id) in alloc.relocations().values() { - collect_miri(tcx, id, &mut neighbors); - } - } - } - MonoItem::Fn(instance) => { - // Sanity check whether this ended up being collected accidentally - debug_assert!(should_codegen_locally(tcx, &instance)); - - // Keep track of the monomorphization recursion depth - recursion_depth_reset = - Some(check_recursion_limit(tcx, instance, starting_point.span, recursion_depths)); - check_type_length_limit(tcx, instance); - - rustc_data_structures::stack::ensure_sufficient_stack(|| { - collect_neighbours(tcx, instance, &mut neighbors); - }); - } - MonoItem::GlobalAsm(..) => { - recursion_depth_reset = None; - } - } - - record_accesses(tcx, starting_point.node, neighbors.iter().map(|i| &i.node), inlining_map); - - for neighbour in neighbors { - collect_items_rec(tcx, neighbour, visited, recursion_depths, inlining_map); - } - - if let Some((def_id, depth)) = recursion_depth_reset { - recursion_depths.insert(def_id, depth); - } - - debug!("END collect_items_rec({})", starting_point.node); -} - -fn record_accesses<'a, 'tcx: 'a>( - tcx: TyCtxt<'tcx>, - caller: MonoItem<'tcx>, - callees: impl Iterator>, - inlining_map: MTRef<'_, MTLock>>, -) { - let is_inlining_candidate = |mono_item: &MonoItem<'tcx>| { - mono_item.instantiation_mode(tcx) == InstantiationMode::LocalCopy - }; - - // We collect this into a `SmallVec` to avoid calling `is_inlining_candidate` in the lock. - // FIXME: Call `is_inlining_candidate` when pushing to `neighbors` in `collect_items_rec` - // instead to avoid creating this `SmallVec`. - let accesses: SmallVec<[_; 128]> = - callees.map(|mono_item| (*mono_item, is_inlining_candidate(mono_item))).collect(); - - inlining_map.lock_mut().record_accesses(caller, &accesses); -} - -/// Format instance name that is already known to be too long for rustc. -/// Show only the first and last 32 characters to avoid blasting -/// the user's terminal with thousands of lines of type-name. -/// -/// If the type name is longer than before+after, it will be written to a file. -fn shrunk_instance_name( - tcx: TyCtxt<'tcx>, - instance: &Instance<'tcx>, - before: usize, - after: usize, -) -> (String, Option) { - let s = instance.to_string(); - - // Only use the shrunk version if it's really shorter. - // This also avoids the case where before and after slices overlap. - if s.chars().nth(before + after + 1).is_some() { - // An iterator of all byte positions including the end of the string. - let positions = || s.char_indices().map(|(i, _)| i).chain(iter::once(s.len())); - - let shrunk = format!( - "{before}...{after}", - before = &s[..positions().nth(before).unwrap_or(s.len())], - after = &s[positions().rev().nth(after).unwrap_or(0)..], - ); - - let path = tcx.output_filenames(LOCAL_CRATE).temp_path_ext("long-type.txt", None); - let written_to_path = std::fs::write(&path, s).ok().map(|_| path); - - (shrunk, written_to_path) - } else { - (s, None) - } -} - -fn check_recursion_limit<'tcx>( - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - span: Span, - recursion_depths: &mut DefIdMap, -) -> (DefId, usize) { - let def_id = instance.def_id(); - let recursion_depth = recursion_depths.get(&def_id).cloned().unwrap_or(0); - debug!(" => recursion depth={}", recursion_depth); - - let adjusted_recursion_depth = if Some(def_id) == tcx.lang_items().drop_in_place_fn() { - // HACK: drop_in_place creates tight monomorphization loops. Give - // it more margin. - recursion_depth / 4 - } else { - recursion_depth - }; - - // Code that needs to instantiate the same function recursively - // more than the recursion limit is assumed to be causing an - // infinite expansion. - if !tcx.sess.recursion_limit().value_within_limit(adjusted_recursion_depth) { - let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance, 32, 32); - let error = format!("reached the recursion limit while instantiating `{}`", shrunk); - let mut err = tcx.sess.struct_span_fatal(span, &error); - err.span_note( - tcx.def_span(def_id), - &format!("`{}` defined here", tcx.def_path_str(def_id)), - ); - if let Some(path) = written_to_path { - err.note(&format!("the full type name has been written to '{}'", path.display())); - } - err.emit(); - FatalError.raise(); - } - - recursion_depths.insert(def_id, recursion_depth + 1); - - (def_id, recursion_depth) -} - -fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { - let type_length = instance - .substs - .iter() - .flat_map(|arg| arg.walk()) - .filter(|arg| match arg.unpack() { - GenericArgKind::Type(_) | GenericArgKind::Const(_) => true, - GenericArgKind::Lifetime(_) => false, - }) - .count(); - debug!(" => type length={}", type_length); - - // Rust code can easily create exponentially-long types using only a - // polynomial recursion depth. Even with the default recursion - // depth, you can easily get cases that take >2^60 steps to run, - // which means that rustc basically hangs. - // - // Bail out in these cases to avoid that bad user experience. - if !tcx.sess.type_length_limit().value_within_limit(type_length) { - let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance, 32, 32); - let msg = format!("reached the type-length limit while instantiating `{}`", shrunk); - let mut diag = tcx.sess.struct_span_fatal(tcx.def_span(instance.def_id()), &msg); - if let Some(path) = written_to_path { - diag.note(&format!("the full type name has been written to '{}'", path.display())); - } - diag.help(&format!( - "consider adding a `#![type_length_limit=\"{}\"]` attribute to your crate", - type_length - )); - diag.emit(); - tcx.sess.abort_if_errors(); - } -} - -struct MirNeighborCollector<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - body: &'a mir::Body<'tcx>, - output: &'a mut Vec>>, - instance: Instance<'tcx>, -} - -impl<'a, 'tcx> MirNeighborCollector<'a, 'tcx> { - pub fn monomorphize(&self, value: T) -> T - where - T: TypeFoldable<'tcx>, - { - debug!("monomorphize: self.instance={:?}", self.instance); - self.instance.subst_mir_and_normalize_erasing_regions( - self.tcx, - ty::ParamEnv::reveal_all(), - value, - ) - } -} - -impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { - fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { - debug!("visiting rvalue {:?}", *rvalue); - - let span = self.body.source_info(location).span; - - match *rvalue { - // When doing an cast from a regular pointer to a fat pointer, we - // have to instantiate all methods of the trait being cast to, so we - // can build the appropriate vtable. - mir::Rvalue::Cast( - mir::CastKind::Pointer(PointerCast::Unsize), - ref operand, - target_ty, - ) => { - let target_ty = self.monomorphize(target_ty); - let source_ty = operand.ty(self.body, self.tcx); - let source_ty = self.monomorphize(source_ty); - let (source_ty, target_ty) = - find_vtable_types_for_unsizing(self.tcx, source_ty, target_ty); - // This could also be a different Unsize instruction, like - // from a fixed sized array to a slice. But we are only - // interested in things that produce a vtable. - if target_ty.is_trait() && !source_ty.is_trait() { - create_mono_items_for_vtable_methods( - self.tcx, - target_ty, - source_ty, - span, - self.output, - ); - } - } - mir::Rvalue::Cast( - mir::CastKind::Pointer(PointerCast::ReifyFnPointer), - ref operand, - _, - ) => { - let fn_ty = operand.ty(self.body, self.tcx); - let fn_ty = self.monomorphize(fn_ty); - visit_fn_use(self.tcx, fn_ty, false, span, &mut self.output); - } - mir::Rvalue::Cast( - mir::CastKind::Pointer(PointerCast::ClosureFnPointer(_)), - ref operand, - _, - ) => { - let source_ty = operand.ty(self.body, self.tcx); - let source_ty = self.monomorphize(source_ty); - match *source_ty.kind() { - ty::Closure(def_id, substs) => { - let instance = Instance::resolve_closure( - self.tcx, - def_id, - substs, - ty::ClosureKind::FnOnce, - ); - if should_codegen_locally(self.tcx, &instance) { - self.output.push(create_fn_mono_item(self.tcx, instance, span)); - } - } - _ => bug!(), - } - } - mir::Rvalue::NullaryOp(mir::NullOp::Box, _) => { - let tcx = self.tcx; - let exchange_malloc_fn_def_id = - tcx.require_lang_item(LangItem::ExchangeMalloc, None); - let instance = Instance::mono(tcx, exchange_malloc_fn_def_id); - if should_codegen_locally(tcx, &instance) { - self.output.push(create_fn_mono_item(self.tcx, instance, span)); - } - } - mir::Rvalue::ThreadLocalRef(def_id) => { - assert!(self.tcx.is_thread_local_static(def_id)); - let instance = Instance::mono(self.tcx, def_id); - if should_codegen_locally(self.tcx, &instance) { - trace!("collecting thread-local static {:?}", def_id); - self.output.push(respan(span, MonoItem::Static(def_id))); - } - } - _ => { /* not interesting */ } - } - - self.super_rvalue(rvalue, location); - } - - fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, location: Location) { - debug!("visiting const {:?} @ {:?}", *constant, location); - - let substituted_constant = self.monomorphize(*constant); - let param_env = ty::ParamEnv::reveal_all(); - - match substituted_constant.val { - ty::ConstKind::Value(val) => collect_const_value(self.tcx, val, self.output), - ty::ConstKind::Unevaluated(def, substs, promoted) => { - match self.tcx.const_eval_resolve(param_env, def, substs, promoted, None) { - Ok(val) => collect_const_value(self.tcx, val, self.output), - Err(ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted) => {} - Err(ErrorHandled::TooGeneric) => span_bug!( - self.body.source_info(location).span, - "collection encountered polymorphic constant: {}", - substituted_constant - ), - Err(ErrorHandled::Silent) => span_bug!( - self.body.source_info(location).span, - "silent error emitted during collection", - ), - } - } - _ => {} - } - - self.super_const(constant); - } - - fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { - debug!("visiting terminator {:?} @ {:?}", terminator, location); - let source = self.body.source_info(location).span; - - let tcx = self.tcx; - match terminator.kind { - mir::TerminatorKind::Call { ref func, .. } => { - let callee_ty = func.ty(self.body, tcx); - let callee_ty = self.monomorphize(callee_ty); - visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output); - } - mir::TerminatorKind::Drop { ref place, .. } - | mir::TerminatorKind::DropAndReplace { ref place, .. } => { - let ty = place.ty(self.body, self.tcx).ty; - let ty = self.monomorphize(ty); - visit_drop_use(self.tcx, ty, true, source, self.output); - } - mir::TerminatorKind::InlineAsm { ref operands, .. } => { - for op in operands { - match *op { - mir::InlineAsmOperand::SymFn { ref value } => { - let fn_ty = self.monomorphize(value.literal.ty); - visit_fn_use(self.tcx, fn_ty, false, source, &mut self.output); - } - mir::InlineAsmOperand::SymStatic { def_id } => { - let instance = Instance::mono(self.tcx, def_id); - if should_codegen_locally(self.tcx, &instance) { - trace!("collecting asm sym static {:?}", def_id); - self.output.push(respan(source, MonoItem::Static(def_id))); - } - } - _ => {} - } - } - } - mir::TerminatorKind::Goto { .. } - | mir::TerminatorKind::SwitchInt { .. } - | mir::TerminatorKind::Resume - | mir::TerminatorKind::Abort - | mir::TerminatorKind::Return - | mir::TerminatorKind::Unreachable - | mir::TerminatorKind::Assert { .. } => {} - mir::TerminatorKind::GeneratorDrop - | mir::TerminatorKind::Yield { .. } - | mir::TerminatorKind::FalseEdge { .. } - | mir::TerminatorKind::FalseUnwind { .. } => bug!(), - } - - self.super_terminator(terminator, location); - } - - fn visit_local( - &mut self, - _place_local: &Local, - _context: mir::visit::PlaceContext, - _location: Location, - ) { - } -} - -fn visit_drop_use<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - is_direct_call: bool, - source: Span, - output: &mut Vec>>, -) { - let instance = Instance::resolve_drop_in_place(tcx, ty); - visit_instance_use(tcx, instance, is_direct_call, source, output); -} - -fn visit_fn_use<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - is_direct_call: bool, - source: Span, - output: &mut Vec>>, -) { - if let ty::FnDef(def_id, substs) = *ty.kind() { - let instance = if is_direct_call { - ty::Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, substs).unwrap().unwrap() - } else { - ty::Instance::resolve_for_fn_ptr(tcx, ty::ParamEnv::reveal_all(), def_id, substs) - .unwrap() - }; - visit_instance_use(tcx, instance, is_direct_call, source, output); - } -} - -fn visit_instance_use<'tcx>( - tcx: TyCtxt<'tcx>, - instance: ty::Instance<'tcx>, - is_direct_call: bool, - source: Span, - output: &mut Vec>>, -) { - debug!("visit_item_use({:?}, is_direct_call={:?})", instance, is_direct_call); - if !should_codegen_locally(tcx, &instance) { - return; - } - - match instance.def { - ty::InstanceDef::Virtual(..) | ty::InstanceDef::Intrinsic(_) => { - if !is_direct_call { - bug!("{:?} being reified", instance); - } - } - ty::InstanceDef::DropGlue(_, None) => { - // Don't need to emit noop drop glue if we are calling directly. - if !is_direct_call { - output.push(create_fn_mono_item(tcx, instance, source)); - } - } - ty::InstanceDef::DropGlue(_, Some(_)) - | ty::InstanceDef::VtableShim(..) - | ty::InstanceDef::ReifyShim(..) - | ty::InstanceDef::ClosureOnceShim { .. } - | ty::InstanceDef::Item(..) - | ty::InstanceDef::FnPtrShim(..) - | ty::InstanceDef::CloneShim(..) => { - output.push(create_fn_mono_item(tcx, instance, source)); - } - } -} - -// Returns `true` if we should codegen an instance in the local crate. -// Returns `false` if we can just link to the upstream crate and therefore don't -// need a mono item. -fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> bool { - let def_id = match instance.def { - ty::InstanceDef::Item(def) => def.did, - ty::InstanceDef::DropGlue(def_id, Some(_)) => def_id, - ty::InstanceDef::VtableShim(..) - | ty::InstanceDef::ReifyShim(..) - | ty::InstanceDef::ClosureOnceShim { .. } - | ty::InstanceDef::Virtual(..) - | ty::InstanceDef::FnPtrShim(..) - | ty::InstanceDef::DropGlue(..) - | ty::InstanceDef::Intrinsic(_) - | ty::InstanceDef::CloneShim(..) => return true, - }; - - if tcx.is_foreign_item(def_id) { - // Foreign items are always linked against, there's no way of instantiating them. - return false; - } - - if def_id.is_local() { - // Local items cannot be referred to locally without monomorphizing them locally. - return true; - } - - if tcx.is_reachable_non_generic(def_id) - || instance.polymorphize(tcx).upstream_monomorphization(tcx).is_some() - { - // We can link to the item in question, no instance needed in this crate. - return false; - } - - if !tcx.is_mir_available(def_id) { - bug!("no MIR available for {:?}", def_id); - } - - true -} - -/// For a given pair of source and target type that occur in an unsizing coercion, -/// this function finds the pair of types that determines the vtable linking -/// them. -/// -/// For example, the source type might be `&SomeStruct` and the target type\ -/// might be `&SomeTrait` in a cast like: -/// -/// let src: &SomeStruct = ...; -/// let target = src as &SomeTrait; -/// -/// Then the output of this function would be (SomeStruct, SomeTrait) since for -/// constructing the `target` fat-pointer we need the vtable for that pair. -/// -/// Things can get more complicated though because there's also the case where -/// the unsized type occurs as a field: -/// -/// ```rust -/// struct ComplexStruct { -/// a: u32, -/// b: f64, -/// c: T -/// } -/// ``` -/// -/// In this case, if `T` is sized, `&ComplexStruct` is a thin pointer. If `T` -/// is unsized, `&SomeStruct` is a fat pointer, and the vtable it points to is -/// for the pair of `T` (which is a trait) and the concrete type that `T` was -/// originally coerced from: -/// -/// let src: &ComplexStruct = ...; -/// let target = src as &ComplexStruct; -/// -/// Again, we want this `find_vtable_types_for_unsizing()` to provide the pair -/// `(SomeStruct, SomeTrait)`. -/// -/// Finally, there is also the case of custom unsizing coercions, e.g., for -/// smart pointers such as `Rc` and `Arc`. -fn find_vtable_types_for_unsizing<'tcx>( - tcx: TyCtxt<'tcx>, - source_ty: Ty<'tcx>, - target_ty: Ty<'tcx>, -) -> (Ty<'tcx>, Ty<'tcx>) { - let ptr_vtable = |inner_source: Ty<'tcx>, inner_target: Ty<'tcx>| { - let param_env = ty::ParamEnv::reveal_all(); - let type_has_metadata = |ty: Ty<'tcx>| -> bool { - if ty.is_sized(tcx.at(DUMMY_SP), param_env) { - return false; - } - let tail = tcx.struct_tail_erasing_lifetimes(ty, param_env); - match tail.kind() { - ty::Foreign(..) => false, - ty::Str | ty::Slice(..) | ty::Dynamic(..) => true, - _ => bug!("unexpected unsized tail: {:?}", tail), - } - }; - if type_has_metadata(inner_source) { - (inner_source, inner_target) - } else { - tcx.struct_lockstep_tails_erasing_lifetimes(inner_source, inner_target, param_env) - } - }; - - match (&source_ty.kind(), &target_ty.kind()) { - (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) - | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { - ptr_vtable(a, b) - } - (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => { - ptr_vtable(source_ty.boxed_ty(), target_ty.boxed_ty()) - } - - (&ty::Adt(source_adt_def, source_substs), &ty::Adt(target_adt_def, target_substs)) => { - assert_eq!(source_adt_def, target_adt_def); - - let CustomCoerceUnsized::Struct(coerce_index) = - monomorphize::custom_coerce_unsize_info(tcx, source_ty, target_ty); - - let source_fields = &source_adt_def.non_enum_variant().fields; - let target_fields = &target_adt_def.non_enum_variant().fields; - - assert!( - coerce_index < source_fields.len() && source_fields.len() == target_fields.len() - ); - - find_vtable_types_for_unsizing( - tcx, - source_fields[coerce_index].ty(tcx, source_substs), - target_fields[coerce_index].ty(tcx, target_substs), - ) - } - _ => bug!( - "find_vtable_types_for_unsizing: invalid coercion {:?} -> {:?}", - source_ty, - target_ty - ), - } -} - -fn create_fn_mono_item<'tcx>( - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - source: Span, -) -> Spanned> { - debug!("create_fn_mono_item(instance={})", instance); - respan(source, MonoItem::Fn(instance.polymorphize(tcx))) -} - -/// Creates a `MonoItem` for each method that is referenced by the vtable for -/// the given trait/impl pair. -fn create_mono_items_for_vtable_methods<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ty: Ty<'tcx>, - impl_ty: Ty<'tcx>, - source: Span, - output: &mut Vec>>, -) { - assert!(!trait_ty.has_escaping_bound_vars() && !impl_ty.has_escaping_bound_vars()); - - if let ty::Dynamic(ref trait_ty, ..) = trait_ty.kind() { - if let Some(principal) = trait_ty.principal() { - let poly_trait_ref = principal.with_self_ty(tcx, impl_ty); - assert!(!poly_trait_ref.has_escaping_bound_vars()); - - // Walk all methods of the trait, including those of its supertraits - let methods = tcx.vtable_methods(poly_trait_ref); - let methods = methods - .iter() - .cloned() - .filter_map(|method| method) - .map(|(def_id, substs)| { - ty::Instance::resolve_for_vtable( - tcx, - ty::ParamEnv::reveal_all(), - def_id, - substs, - ) - .unwrap() - }) - .filter(|&instance| should_codegen_locally(tcx, &instance)) - .map(|item| create_fn_mono_item(tcx, item, source)); - output.extend(methods); - } - - // Also add the destructor. - visit_drop_use(tcx, impl_ty, false, source, output); - } -} - -//=----------------------------------------------------------------------------- -// Root Collection -//=----------------------------------------------------------------------------- - -struct RootCollector<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - mode: MonoItemCollectionMode, - output: &'a mut Vec>>, - entry_fn: Option<(LocalDefId, EntryFnType)>, -} - -impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> { - fn visit_item(&mut self, item: &'v hir::Item<'v>) { - match item.kind { - hir::ItemKind::ExternCrate(..) - | hir::ItemKind::Use(..) - | hir::ItemKind::ForeignMod { .. } - | hir::ItemKind::TyAlias(..) - | hir::ItemKind::Trait(..) - | hir::ItemKind::TraitAlias(..) - | hir::ItemKind::OpaqueTy(..) - | hir::ItemKind::Mod(..) => { - // Nothing to do, just keep recursing. - } - - hir::ItemKind::Impl { .. } => { - if self.mode == MonoItemCollectionMode::Eager { - create_mono_items_for_default_impls(self.tcx, item, self.output); - } - } - - hir::ItemKind::Enum(_, ref generics) - | hir::ItemKind::Struct(_, ref generics) - | hir::ItemKind::Union(_, ref generics) => { - if generics.params.is_empty() { - if self.mode == MonoItemCollectionMode::Eager { - let def_id = self.tcx.hir().local_def_id(item.hir_id); - debug!( - "RootCollector: ADT drop-glue for {}", - self.tcx.def_path_str(def_id.to_def_id()) - ); - - let ty = Instance::new(def_id.to_def_id(), InternalSubsts::empty()) - .ty(self.tcx, ty::ParamEnv::reveal_all()); - visit_drop_use(self.tcx, ty, true, DUMMY_SP, self.output); - } - } - } - hir::ItemKind::GlobalAsm(..) => { - debug!( - "RootCollector: ItemKind::GlobalAsm({})", - self.tcx.def_path_str(self.tcx.hir().local_def_id(item.hir_id).to_def_id()) - ); - self.output.push(dummy_spanned(MonoItem::GlobalAsm(item.hir_id))); - } - hir::ItemKind::Static(..) => { - let def_id = self.tcx.hir().local_def_id(item.hir_id).to_def_id(); - debug!("RootCollector: ItemKind::Static({})", self.tcx.def_path_str(def_id)); - self.output.push(dummy_spanned(MonoItem::Static(def_id))); - } - hir::ItemKind::Const(..) => { - // const items only generate mono items if they are - // actually used somewhere. Just declaring them is insufficient. - - // but even just declaring them must collect the items they refer to - let def_id = self.tcx.hir().local_def_id(item.hir_id); - - if let Ok(val) = self.tcx.const_eval_poly(def_id.to_def_id()) { - collect_const_value(self.tcx, val, &mut self.output); - } - } - hir::ItemKind::Fn(..) => { - let def_id = self.tcx.hir().local_def_id(item.hir_id); - self.push_if_root(def_id); - } - } - } - - fn visit_trait_item(&mut self, _: &'v hir::TraitItem<'v>) { - // Even if there's a default body with no explicit generics, - // it's still generic over some `Self: Trait`, so not a root. - } - - fn visit_impl_item(&mut self, ii: &'v hir::ImplItem<'v>) { - if let hir::ImplItemKind::Fn(hir::FnSig { .. }, _) = ii.kind { - let def_id = self.tcx.hir().local_def_id(ii.hir_id); - self.push_if_root(def_id); - } - } - - fn visit_foreign_item(&mut self, _foreign_item: &'v hir::ForeignItem<'v>) {} -} - -impl RootCollector<'_, 'v> { - fn is_root(&self, def_id: LocalDefId) -> bool { - !item_requires_monomorphization(self.tcx, def_id) - && match self.mode { - MonoItemCollectionMode::Eager => true, - MonoItemCollectionMode::Lazy => { - self.entry_fn.map(|(id, _)| id) == Some(def_id) - || self.tcx.is_reachable_non_generic(def_id) - || self - .tcx - .codegen_fn_attrs(def_id) - .flags - .contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) - } - } - } - - /// If `def_id` represents a root, pushes it onto the list of - /// outputs. (Note that all roots must be monomorphic.) - fn push_if_root(&mut self, def_id: LocalDefId) { - if self.is_root(def_id) { - debug!("RootCollector::push_if_root: found root def_id={:?}", def_id); - - let instance = Instance::mono(self.tcx, def_id.to_def_id()); - self.output.push(create_fn_mono_item(self.tcx, instance, DUMMY_SP)); - } - } - - /// As a special case, when/if we encounter the - /// `main()` function, we also have to generate a - /// monomorphized copy of the start lang item based on - /// the return type of `main`. This is not needed when - /// the user writes their own `start` manually. - fn push_extra_entry_roots(&mut self) { - let main_def_id = match self.entry_fn { - Some((def_id, EntryFnType::Main)) => def_id, - _ => return, - }; - - let start_def_id = match self.tcx.lang_items().require(LangItem::Start) { - Ok(s) => s, - Err(err) => self.tcx.sess.fatal(&err), - }; - let main_ret_ty = self.tcx.fn_sig(main_def_id).output(); - - // Given that `main()` has no arguments, - // then its return type cannot have - // late-bound regions, since late-bound - // regions must appear in the argument - // listing. - let main_ret_ty = self.tcx.erase_regions(main_ret_ty.no_bound_vars().unwrap()); - - let start_instance = Instance::resolve( - self.tcx, - ty::ParamEnv::reveal_all(), - start_def_id, - self.tcx.intern_substs(&[main_ret_ty.into()]), - ) - .unwrap() - .unwrap(); - - self.output.push(create_fn_mono_item(self.tcx, start_instance, DUMMY_SP)); - } -} - -fn item_requires_monomorphization(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { - let generics = tcx.generics_of(def_id); - generics.requires_monomorphization(tcx) -} - -fn create_mono_items_for_default_impls<'tcx>( - tcx: TyCtxt<'tcx>, - item: &'tcx hir::Item<'tcx>, - output: &mut Vec>>, -) { - match item.kind { - hir::ItemKind::Impl(ref impl_) => { - for param in impl_.generics.params { - match param.kind { - hir::GenericParamKind::Lifetime { .. } => {} - hir::GenericParamKind::Type { .. } | hir::GenericParamKind::Const { .. } => { - return; - } - } - } - - let impl_def_id = tcx.hir().local_def_id(item.hir_id); - - debug!( - "create_mono_items_for_default_impls(item={})", - tcx.def_path_str(impl_def_id.to_def_id()) - ); - - if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) { - let param_env = ty::ParamEnv::reveal_all(); - let trait_ref = tcx.normalize_erasing_regions(param_env, trait_ref); - let overridden_methods: FxHashSet<_> = - impl_.items.iter().map(|iiref| iiref.ident.normalize_to_macros_2_0()).collect(); - for method in tcx.provided_trait_methods(trait_ref.def_id) { - if overridden_methods.contains(&method.ident.normalize_to_macros_2_0()) { - continue; - } - - if tcx.generics_of(method.def_id).own_requires_monomorphization() { - continue; - } - - let substs = - InternalSubsts::for_item(tcx, method.def_id, |param, _| match param.kind { - GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { - trait_ref.substs[param.index as usize] - } - }); - let instance = ty::Instance::resolve(tcx, param_env, method.def_id, substs) - .unwrap() - .unwrap(); - - let mono_item = create_fn_mono_item(tcx, instance, DUMMY_SP); - if mono_item.node.is_instantiable(tcx) && should_codegen_locally(tcx, &instance) - { - output.push(mono_item); - } - } - } - } - _ => bug!(), - } -} - -/// Scans the miri alloc in order to find function calls, closures, and drop-glue. -fn collect_miri<'tcx>( - tcx: TyCtxt<'tcx>, - alloc_id: AllocId, - output: &mut Vec>>, -) { - match tcx.global_alloc(alloc_id) { - GlobalAlloc::Static(def_id) => { - assert!(!tcx.is_thread_local_static(def_id)); - let instance = Instance::mono(tcx, def_id); - if should_codegen_locally(tcx, &instance) { - trace!("collecting static {:?}", def_id); - output.push(dummy_spanned(MonoItem::Static(def_id))); - } - } - GlobalAlloc::Memory(alloc) => { - trace!("collecting {:?} with {:#?}", alloc_id, alloc); - for &((), inner) in alloc.relocations().values() { - rustc_data_structures::stack::ensure_sufficient_stack(|| { - collect_miri(tcx, inner, output); - }); - } - } - GlobalAlloc::Function(fn_instance) => { - if should_codegen_locally(tcx, &fn_instance) { - trace!("collecting {:?} with {:#?}", alloc_id, fn_instance); - output.push(create_fn_mono_item(tcx, fn_instance, DUMMY_SP)); - } - } - } -} - -/// Scans the MIR in order to find function calls, closures, and drop-glue. -fn collect_neighbours<'tcx>( - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - output: &mut Vec>>, -) { - debug!("collect_neighbours: {:?}", instance.def_id()); - let body = tcx.instance_mir(instance.def); - - MirNeighborCollector { tcx, body: &body, output, instance }.visit_body(&body); -} - -fn collect_const_value<'tcx>( - tcx: TyCtxt<'tcx>, - value: ConstValue<'tcx>, - output: &mut Vec>>, -) { - match value { - ConstValue::Scalar(Scalar::Ptr(ptr)) => collect_miri(tcx, ptr.alloc_id, output), - ConstValue::Slice { data: alloc, start: _, end: _ } | ConstValue::ByRef { alloc, .. } => { - for &((), id) in alloc.relocations().values() { - collect_miri(tcx, id, output); - } - } - _ => {} - } -} From 773101fca543e6ed0962a00ed6f85baca1c980bf Mon Sep 17 00:00:00 2001 From: b-naber Date: Fri, 7 Jan 2022 20:17:54 +0100 Subject: [PATCH 06/11] update queries for deduplication --- .../src/const_eval/eval_queries.rs | 133 +++++++++++------- .../src/interpret/eval_context.rs | 24 ++-- .../rustc_middle/src/mir/interpret/queries.rs | 18 ++- 3 files changed, 111 insertions(+), 64 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index c5412affafe23..59d448359ad52 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -10,10 +10,10 @@ use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_middle::mir; -use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::mir::interpret::{ConstDedupResult, ErrorHandled}; use rustc_middle::mir::pretty::display_allocation; use rustc_middle::traits::Reveal; -use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::layout::{LayoutError, LayoutOf}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, subst::Subst, TyCtxt}; use rustc_span::source_map::Span; @@ -28,6 +28,7 @@ pub fn note_on_undefined_behavior_error() -> &'static str { } // Returns a pointer to where the result lives +#[instrument(skip(ecx, body), level = "debug")] fn eval_body_using_ecx<'mir, 'tcx>( ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, cid: GlobalId<'tcx>, @@ -160,6 +161,7 @@ pub(super) fn op_to_const<'tcx>( ConstValue::Scalar(Scalar::ZST) } }; + match immediate { Ok(ref mplace) => to_const_value(mplace), // see comment on `let try_as_immediate` above @@ -212,77 +214,76 @@ fn turn_into_const_value<'tcx>( op_to_const(&ecx, &mplace.into()) } +#[instrument(skip(tcx), level = "debug")] pub fn eval_to_const_value_raw_provider<'tcx>( tcx: TyCtxt<'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, ) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> { assert!(key.param_env.constness() == hir::Constness::Const); - // see comment in eval_to_allocation_raw_provider for what we're doing here - if key.param_env.reveal() == Reveal::All { - let mut key = key; - key.param_env = key.param_env.with_user_facing(); - match tcx.eval_to_const_value_raw(key) { - // try again with reveal all as requested - Err(ErrorHandled::TooGeneric) => {} - // deduplicate calls - other => return other, - } - } + let (param_env, id) = key.into_parts(); + let reveal = param_env.reveal(); // We call `const_eval` for zero arg intrinsics, too, in order to cache their value. // Catch such calls and evaluate them instead of trying to load a constant's MIR. if let ty::InstanceDef::Intrinsic(def_id) = key.value.instance.def { - let ty = key.value.instance.ty(tcx, key.param_env); + let ty = key.value.instance.ty(tcx, param_env); let substs = match ty.kind() { ty::FnDef(_, substs) => substs, _ => bug!("intrinsic with type {:?}", ty), }; - return eval_nullary_intrinsic(tcx, key.param_env, def_id, substs).map_err(|error| { - let span = tcx.def_span(def_id); - let error = ConstEvalErr { error: error.into_kind(), stacktrace: vec![], span }; - error.report_as_error(tcx.at(span), "could not evaluate nullary intrinsic") - }); + + match eval_nullary_intrinsic(tcx, param_env, def_id, substs) { + Ok(val) => { + // store result for deduplication + let res = ConstDedupResult::new(reveal, val); + tcx.save_const_value_for_dedup(id, res); + + return Ok(val); + } + Err(e) => { + let span = tcx.def_span(def_id); + let error = ConstEvalErr { error: e.into_kind(), stacktrace: vec![], span }; + + return Err( + error.report_as_error(tcx.at(span), "could not evaluate nullary intrinsic") + ); + } + } + } + + let result = + tcx.dedup_eval_alloc_raw(key, None).map(|val| turn_into_const_value(tcx, val, key)); + + match result { + Ok(val) => { + tcx.save_const_value_for_dedup(id, ConstDedupResult::new(reveal, val)); + } + _ => {} } - tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key)) + result } +#[instrument(skip(tcx), level = "debug")] pub fn eval_to_allocation_raw_provider<'tcx>( tcx: TyCtxt<'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, ) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> { assert!(key.param_env.constness() == hir::Constness::Const); - // Because the constant is computed twice (once per value of `Reveal`), we are at risk of - // reporting the same error twice here. To resolve this, we check whether we can evaluate the - // constant in the more restrictive `Reveal::UserFacing`, which most likely already was - // computed. For a large percentage of constants that will already have succeeded. Only - // associated constants of generic functions will fail due to not enough monomorphization - // information being available. - - // In case we fail in the `UserFacing` variant, we just do the real computation. - if key.param_env.reveal() == Reveal::All { - let mut key = key; - key.param_env = key.param_env.with_user_facing(); - match tcx.eval_to_allocation_raw(key) { - // try again with reveal all as requested - Err(ErrorHandled::TooGeneric) => {} - // deduplicate calls - other => return other, - } - } + let (param_env, cid) = key.into_parts(); + let reveal = param_env.reveal(); + let def = cid.instance.def.with_opt_param(); + if cfg!(debug_assertions) { // Make sure we format the instance even if we do not print it. // This serves as a regression test against an ICE on printing. // The next two lines concatenated contain some discussion: // https://rust-lang.zulipchat.com/#narrow/stream/146212-t-compiler.2Fconst-eval/ // subject/anon_const_instance_printing/near/135980032 - let instance = with_no_trimmed_paths(|| key.value.instance.to_string()); + let instance = with_no_trimmed_paths(|| cid.instance.to_string()); trace!("const eval: {:?} ({})", key, instance); } - let cid = key.value; - let def = cid.instance.def.with_opt_param(); - if let Some(def) = def.as_local() { if tcx.has_typeck_results(def.did) { if let Some(error_reported) = tcx.typeck_opt_const_arg(def).tainted_by_errors { @@ -316,6 +317,20 @@ pub fn eval_to_allocation_raw_provider<'tcx>( let res = ecx.load_mir(cid.instance.def, cid.promoted); match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) { Err(error) => { + debug!("error from eval_body_using_ecx: {:?}", error); + if reveal == Reveal::Selection { + match error.kind() { + err_inval!(Layout(LayoutError::Unknown(_))) + | err_inval!(TooGeneric) + | err_inval!(AlreadyReported(_)) => { + // We do want to report these errors + } + _ => { + return Err(ErrorHandled::Silent); + } + } + } + let err = ConstEvalErr::new(&ecx, error, None); // Some CTFE errors raise just a lint, not a hard error; see // . @@ -328,7 +343,6 @@ pub fn eval_to_allocation_raw_provider<'tcx>( // use of broken constant from other crate: always an error true }; - if is_hard_err { let msg = if is_static { Cow::from("could not evaluate static initializer") @@ -345,7 +359,6 @@ pub fn eval_to_allocation_raw_provider<'tcx>( Cow::from("evaluation of constant value failed") } }; - Err(err.report_as_error(ecx.tcx.at(ecx.cur_span()), &msg)) } else { let hir_id = tcx.hir().local_def_id_to_hir_id(def.as_local().unwrap().did); @@ -377,10 +390,26 @@ pub fn eval_to_allocation_raw_provider<'tcx>( } }; let alloc_id = mplace.ptr.provenance.unwrap(); + if let Err(error) = validation { // Validation failed, report an error. This is always a hard error. let err = ConstEvalErr::new(&ecx, error, None); - Err(err.struct_error( + + // FIXME Do we also want to keep these silent with Reveal::Selection? + if reveal == Reveal::Selection { + match err.error { + err_inval!(Layout(LayoutError::Unknown(_))) + | err_inval!(TooGeneric) + | err_inval!(AlreadyReported(_)) => { + // We do want to report these errors + } + _ => { + return Err(ErrorHandled::Silent); + } + } + } + + let error = Err(err.struct_error( ecx.tcx, "it is undefined behavior to use this value", |mut diag| { @@ -394,10 +423,20 @@ pub fn eval_to_allocation_raw_provider<'tcx>( )); diag.emit(); }, - )) + )); + + debug!(?error); + + error } else { // Convert to raw constant - Ok(ConstAlloc { alloc_id, ty: mplace.layout.ty }) + let const_alloc = ConstAlloc { alloc_id, ty: mplace.layout.ty }; + let val = ConstDedupResult::new(reveal, const_alloc); + + // store result in order to deduplicate later + tcx.save_alloc_for_dedup(cid, val); + + Ok(const_alloc) } } } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 246807a112a40..8cfe5da763515 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -18,7 +18,8 @@ use rustc_middle::ty::{ use rustc_mir_dataflow::storage::AlwaysLiveLocals; use rustc_query_system::ich::StableHashingContext; use rustc_session::Limit; -use rustc_span::{Pos, Span}; +use rustc_span::Pos; +use rustc_span::Span; use rustc_target::abi::{call::FnAbi, Align, HasDataLayout, Size, TargetDataLayout}; use super::{ @@ -265,6 +266,13 @@ impl<'mir, 'tcx, Tag: Provenance, Extra> Frame<'mir, 'tcx, Tag, Extra> { } } +impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> { + #[inline] + fn data_layout(&self) -> &TargetDataLayout { + &self.tcx.data_layout + } +} + impl<'tcx> fmt::Display for FrameInfo<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(|tcx| { @@ -291,13 +299,6 @@ impl<'tcx> fmt::Display for FrameInfo<'tcx> { } } -impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> { - #[inline] - fn data_layout(&self) -> &TargetDataLayout { - &self.tcx.data_layout - } -} - impl<'mir, 'tcx, M> layout::HasTyCtxt<'tcx> for InterpCx<'mir, 'tcx, M> where M: Machine<'mir, 'tcx>, @@ -926,6 +927,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(()) } + #[instrument(skip(self), level = "debug")] pub fn eval_to_allocation( &self, gid: GlobalId<'tcx>, @@ -941,8 +943,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.param_env }; let param_env = param_env.with_const(); - let val = self.tcx.eval_to_allocation_raw(param_env.and(gid))?; - self.raw_const_to_mplace(val) + let val = self.tcx.dedup_eval_alloc_raw(param_env.and(gid), None); + debug!(?val); + + self.raw_const_to_mplace(val?) } #[must_use] diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index f983185563315..8edc34164c2c7 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -58,6 +58,7 @@ impl<'tcx> TyCtxt<'tcx> { } /// Evaluate a constant. + #[instrument(skip(self), level = "debug")] pub fn const_eval_global_id( self, param_env: ty::ParamEnv<'tcx>, @@ -68,11 +69,11 @@ impl<'tcx> TyCtxt<'tcx> { // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should // improve caching of queries. let inputs = self.erase_regions(param_env.and(cid)); - if let Some(span) = span { - self.at(span).eval_to_const_value_raw(inputs) - } else { - self.eval_to_const_value_raw(inputs) - } + + let raw_const = self.dedup_eval_const_value_raw(inputs, span); + debug!(?raw_const); + + raw_const } /// Evaluate a static's initializer, returning the allocation of the initializer's memory. @@ -88,6 +89,7 @@ impl<'tcx> TyCtxt<'tcx> { } /// Evaluate anything constant-like, returning the allocation of the final memory. + #[instrument(skip(self), level = "debug")] fn eval_to_allocation( self, gid: GlobalId<'tcx>, @@ -95,7 +97,9 @@ impl<'tcx> TyCtxt<'tcx> { ) -> Result<&'tcx mir::Allocation, ErrorHandled> { let param_env = param_env.with_const(); trace!("eval_to_allocation: Need to compute {:?}", gid); - let raw_const = self.eval_to_allocation_raw(param_env.and(gid))?; - Ok(self.global_alloc(raw_const.alloc_id).unwrap_memory()) + let raw_const = self.dedup_eval_alloc_raw(param_env.and(gid), None); + debug!(?raw_const); + + Ok(self.global_alloc(raw_const?.alloc_id).unwrap_memory()) } } From 8acef875c0a5cdfdd3795cd0ab01fef09dda4e67 Mon Sep 17 00:00:00 2001 From: b-naber Date: Fri, 7 Jan 2022 20:22:22 +0100 Subject: [PATCH 07/11] update NotConstEvaluatable --- compiler/rustc_middle/src/thir/abstract_const.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_middle/src/thir/abstract_const.rs b/compiler/rustc_middle/src/thir/abstract_const.rs index f80beadd6e551..978f16e9fcfb7 100644 --- a/compiler/rustc_middle/src/thir/abstract_const.rs +++ b/compiler/rustc_middle/src/thir/abstract_const.rs @@ -34,6 +34,7 @@ pub enum NotConstEvaluatable { Error(ErrorReported), MentionsInfer, MentionsParam, + Silent, } impl From for NotConstEvaluatable { From 74b2918141d917d24049777bd8c900da2811e460 Mon Sep 17 00:00:00 2001 From: b-naber Date: Fri, 7 Jan 2022 20:23:44 +0100 Subject: [PATCH 08/11] update trait selection --- compiler/rustc_middle/src/ty/consts.rs | 8 ++ .../src/traits/const_evaluatable.rs | 15 ++- .../src/traits/error_reporting/mod.rs | 93 ++++++++++++++++++- .../src/traits/fulfill.rs | 18 ++-- .../rustc_trait_selection/src/traits/mod.rs | 1 + .../src/traits/project.rs | 3 +- .../src/traits/select/mod.rs | 7 +- 7 files changed, 125 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 27e22ccac02a7..278ebdc726c8e 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -257,6 +257,14 @@ impl<'tcx> Const<'tcx> { self.try_eval_usize(tcx, param_env) .unwrap_or_else(|| bug!("expected usize, got {:#?}", self)) } + + #[inline] + pub fn try_get_unevaluated(self) -> Option> { + match self.val { + ConstKind::Unevaluated(uv) => Some(uv), + _ => None, + } + } } pub fn const_param_default<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx Const<'tcx> { diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 710803fead242..6a1b210dbab4f 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -26,12 +26,18 @@ use std::cmp; use std::iter; use std::ops::ControlFlow; +pub enum UseRevealSelection { + Yes, + No, +} + /// Check if a given constant can be evaluated. pub fn is_const_evaluatable<'cx, 'tcx>( infcx: &InferCtxt<'cx, 'tcx>, uv: ty::Unevaluated<'tcx, ()>, param_env: ty::ParamEnv<'tcx>, span: Span, + use_reveal_sel: UseRevealSelection, ) -> Result<(), NotConstEvaluatable> { debug!("is_const_evaluatable({:?})", uv); if infcx.tcx.features().generic_const_exprs { @@ -147,8 +153,12 @@ pub fn is_const_evaluatable<'cx, 'tcx>( // and hopefully soon change this to an error. // // See #74595 for more details about this. - let concrete = - infcx.const_eval_resolve(param_env.with_reveal_selection(), uv.expand(), Some(span)); + let param_env_resolve = match use_reveal_sel { + UseRevealSelection::Yes => param_env.with_reveal_selection(), + UseRevealSelection::No => param_env, + }; + + let concrete = infcx.const_eval_resolve(param_env_resolve, uv.expand(), Some(span)); if concrete.is_ok() && uv.substs(infcx.tcx).definitely_has_param_types_or_consts(infcx.tcx) { match infcx.tcx.def_kind(uv.def.did) { @@ -174,6 +184,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>( Err(NotConstEvaluatable::Error(ErrorReported)) } Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)), + Err(ErrorHandled::Silent) => Err(NotConstEvaluatable::Silent), Ok(_) => Ok(()), } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 19ce6c3a44085..e808ef8c2cbe0 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -2,10 +2,10 @@ pub mod on_unimplemented; pub mod suggestions; use super::{ - EvaluationResult, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, - Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedDirective, - OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, PredicateObligation, - SelectionContext, SelectionError, TraitNotObjectSafe, + const_evaluatable, EvaluationResult, FulfillmentError, FulfillmentErrorCode, + MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, + OnUnimplementedDirective, OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, + PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe, }; use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; @@ -86,6 +86,14 @@ pub trait InferCtxtExt<'tcx> { found_args: Vec, is_closure: bool, ) -> DiagnosticBuilder<'tcx>; + + fn report_const_eval_failure( + &self, + uv: ty::Unevaluated<'tcx, ()>, + obligation: PredicateObligation<'tcx>, + root_obligation: &PredicateObligation<'tcx>, + fallback_has_occurred: bool, + ); } impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { @@ -238,6 +246,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { error: &SelectionError<'tcx>, fallback_has_occurred: bool, ) { + debug!("report_selection_error(error: {:?})", error); let tcx = self.tcx; let mut span = obligation.cause.span; @@ -799,7 +808,42 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { bug!( "MentionsInfer should have been handled in `traits/fulfill.rs` or `traits/select/mod.rs`" ) - ConstEvalFailure(ErrorHandled::Silent) => { + } + SelectionError::NotConstEvaluatable(NotConstEvaluatable::Silent) => { + let pred = obligation.predicate.clone(); + + // Try to prove the obligation again without Reveal::Selection for + // better diagnostics + match pred.kind().skip_binder() { + ty::PredicateKind::ConstEvaluatable(uv) => { + self.report_const_eval_failure( + uv, + obligation.clone(), + root_obligation, + fallback_has_occurred, + ); + } + ty::PredicateKind::ConstEquate(c1, c2) => { + if let Some(uv1) = c1.try_get_unevaluated() { + self.report_const_eval_failure( + uv1.shrink(), + obligation.clone(), + root_obligation, + fallback_has_occurred, + ); + } + + if let Some(uv2) = c2.try_get_unevaluated() { + self.report_const_eval_failure( + uv2.shrink(), + obligation.clone(), + root_obligation, + fallback_has_occurred, + ); + } + } + _ => {} + } tcx.sess.struct_span_err(span, "failed to evaluate the given constant") } SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsParam) => { @@ -1063,6 +1107,45 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err } + + #[instrument(skip(self, obligation, root_obligation, fallback_has_occurred), level = "debug")] + fn report_const_eval_failure( + &self, + uv: ty::Unevaluated<'tcx, ()>, + obligation: PredicateObligation<'tcx>, + root_obligation: &PredicateObligation<'tcx>, + fallback_has_occurred: bool, + ) { + let res = const_evaluatable::is_const_evaluatable( + self, + uv, + obligation.param_env.with_user_facing(), + obligation.cause.span, + const_evaluatable::UseRevealSelection::No, + ); + debug!(?res); + + match res { + Err(NotConstEvaluatable::Silent) => { + bug!("called with Reveal::UserFacing, but Silent error returned") + } + Err(e @ NotConstEvaluatable::MentionsParam | e @ NotConstEvaluatable::Error(_)) => { + let err = SelectionError::NotConstEvaluatable(e); + + self.report_selection_error( + obligation, + root_obligation, + &err, + fallback_has_occurred, + ); + } + Ok(()) => bug!( + "ConstEvaluatable predicate of {:?} failed with Reveal::Selection but succeeded with Reveal::UserFacing?", + uv + ), + _ => {} + } + } } trait InferCtxtPrivExt<'hir, 'tcx> { diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index cb5798b188c64..e841273479ffa 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -528,12 +528,15 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { } ty::PredicateKind::ConstEvaluatable(uv) => { - match const_evaluatable::is_const_evaluatable( + let res = const_evaluatable::is_const_evaluatable( self.selcx.infcx(), uv, - obligation.param_env, + obligation.param_env.with_reveal_selection(), obligation.cause.span, - ) { + const_evaluatable::UseRevealSelection::Yes, + ); + + match res { Ok(()) => ProcessResult::Changed(vec![]), Err(NotConstEvaluatable::MentionsInfer) => { pending_obligation.stalled_on.clear(); @@ -546,7 +549,8 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { } Err( e @ NotConstEvaluatable::MentionsParam - | e @ NotConstEvaluatable::Error(_), + | e @ NotConstEvaluatable::Error(_) + | e @ NotConstEvaluatable::Silent, ) => ProcessResult::Error(CodeSelectionError( SelectionError::NotConstEvaluatable(e), )), @@ -626,9 +630,9 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { ) } (Err(ErrorHandled::Silent), _) | (_, Err(ErrorHandled::Silent)) => { - ProcessResult::Error(CodeSelectionError(ConstEvalFailure( - ErrorHandled::Silent, - ))) + ProcessResult::Error(CodeSelectionError( + SelectionError::NotConstEvaluatable(NotConstEvaluatable::Silent), + )) } (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => { if c1.has_infer_types_or_consts() || c2.has_infer_types_or_consts() { diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index a8f26982d2e42..b7660dddc8ce0 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -273,6 +273,7 @@ fn do_normalize_predicates<'tcx>( // FIXME: this is gonna need to be removed ... /// Normalizes the parameter environment, reporting errors if they occur. +#[instrument(skip(tcx), level = "debug")] pub fn normalize_param_env_or_error<'tcx>( tcx: TyCtxt<'tcx>, region_context: DefId, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 96d5a85f8dbe2..dbd6cf1bd06d4 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -26,6 +26,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; + use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; use rustc_span::symbol::sym; @@ -296,7 +297,7 @@ where pub(crate) fn needs_normalization<'tcx, T: TypeFoldable<'tcx>>(value: &T, reveal: Reveal) -> bool { match reveal { - Reveal::UserFacing => value + Reveal::Selection | Reveal::UserFacing => value .has_type_flags(ty::TypeFlags::HAS_TY_PROJECTION | ty::TypeFlags::HAS_CT_PROJECTION), Reveal::All => value.has_type_flags( ty::TypeFlags::HAS_TY_PROJECTION diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index b7c116fa89b97..6d9bcc2754502 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -625,6 +625,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { uv, obligation.param_env, obligation.cause.span, + const_evaluatable::UseRevealSelection::Yes, ) { Ok(()) => Ok(EvaluatedToOk), Err(NotConstEvaluatable::MentionsInfer) => Ok(EvaluatedToAmbig), @@ -2659,11 +2660,7 @@ impl<'o, 'tcx> TraitObligationStackList<'o, 'tcx> { } fn depth(&self) -> usize { - if let Some(head) = self.head { - head.depth - } else { - 0 - } + if let Some(head) = self.head { head.depth } else { 0 } } } From 53c0bab7d38b05bc9401ad28b8c5b81e23588482 Mon Sep 17 00:00:00 2001 From: b-naber Date: Fri, 7 Jan 2022 20:25:34 +0100 Subject: [PATCH 09/11] update tests --- .../defaults-cyclic-fail.stderr | 20 ----- ...9-assoc-const-static-recursion-impl.stderr | 20 ----- ...onst-static-recursion-trait-default.stderr | 20 ----- ...-assoc-const-static-recursion-trait.stderr | 20 ----- .../from-sig-fail.rs | 12 --- .../from-sig-fail.stderr | 20 ----- .../simple_fail.full.stderr | 17 ----- .../const_evaluatable_checked/simple_fail.rs | 17 ----- .../defaults/default-param-wf-concrete.rs | 1 + .../defaults/default-param-wf-concrete.stderr | 8 +- src/test/ui/const-generics/defaults/wfness.rs | 1 + .../ui/const-generics/defaults/wfness.stderr | 14 +++- .../generic_const_exprs/from-sig-fail.rs | 2 + .../generic_const_exprs/from-sig-fail.stderr | 26 ++++++- .../generic_const_exprs/issue-80742.rs | 1 + .../generic_const_exprs/issue-80742.stderr | 47 ++++++------ .../silent-selection-err.rs | 43 ----------- .../generic_const_exprs/simple_fail.rs | 2 + .../generic_const_exprs/simple_fail.stderr | 20 ++++- .../ui/const-generics/issues/issue-67739.rs | 4 +- .../invalid-patterns.64bit.stderr | 74 ++++++++++++++++++- .../min_const_generics/invalid-patterns.rs | 8 ++ .../const-eval/const-eval-overflow-3.rs | 3 +- .../const-eval/const-eval-overflow-3.stderr | 8 +- .../ui/consts/const-eval/infinite_loop.rs | 1 + .../ui/consts/const-eval/infinite_loop.stderr | 8 +- .../ui/consts/const-eval/issue-49296.stderr | 8 -- src/test/ui/consts/const-eval/issue-52475.rs | 4 +- .../ui/consts/const-eval/issue-52475.stderr | 8 +- .../ui/consts/const-eval/ub-enum.64bit.stderr | 20 ++--- src/test/ui/consts/const-size_of-cycle.stderr | 10 --- src/test/ui/consts/issue-36163.stderr | 20 ----- src/test/ui/consts/issue-44415.stderr | 10 --- src/test/ui/consts/issue-52432.rs | 10 --- src/test/ui/consts/issue-52432.stderr | 28 ------- .../consts/issue-66693-panic-in-array-len.rs | 2 + .../issue-66693-panic-in-array-len.stderr | 18 ++++- .../recursive-zst-static.default.stderr | 12 +-- .../recursive-zst-static.unleash.stderr | 12 +-- .../write-to-static-mut-in-static.stderr | 12 +-- src/test/ui/enum/issue-67945-1.rs | 3 +- src/test/ui/issues/issue-17252.stderr | 10 --- src/test/ui/issues/issue-23302-1.stderr | 10 --- src/test/ui/issues/issue-23302-2.stderr | 10 --- src/test/ui/issues/issue-23302-3.stderr | 20 ----- src/test/ui/mir/issue-80742.rs | 7 +- src/test/ui/mir/issue-80742.stderr | 57 +++++++++----- .../recursive-static-definition.stderr | 12 +-- src/test/ui/treat-err-as-bug/err.stderr | 2 +- .../self-in-enum-definition.stderr | 10 --- 50 files changed, 277 insertions(+), 455 deletions(-) delete mode 100644 src/test/ui/const-generics/const_evaluatable_checked/from-sig-fail.rs delete mode 100644 src/test/ui/const-generics/const_evaluatable_checked/from-sig-fail.stderr delete mode 100644 src/test/ui/const-generics/const_evaluatable_checked/simple_fail.full.stderr delete mode 100644 src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs delete mode 100644 src/test/ui/const-generics/generic_const_exprs/silent-selection-err.rs delete mode 100644 src/test/ui/consts/issue-52432.rs delete mode 100644 src/test/ui/consts/issue-52432.stderr diff --git a/src/test/ui/associated-consts/defaults-cyclic-fail.stderr b/src/test/ui/associated-consts/defaults-cyclic-fail.stderr index 9ae202bf5368b..e5a2777a47bc5 100644 --- a/src/test/ui/associated-consts/defaults-cyclic-fail.stderr +++ b/src/test/ui/associated-consts/defaults-cyclic-fail.stderr @@ -5,16 +5,6 @@ note: ...which requires simplifying constant for the type system `Tr::A`... | LL | const A: u8 = Self::B; | ^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `Tr::A`... - --> $DIR/defaults-cyclic-fail.rs:6:5 - | -LL | const A: u8 = Self::B; - | ^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `Tr::A`... - --> $DIR/defaults-cyclic-fail.rs:6:5 - | -LL | const A: u8 = Self::B; - | ^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `Tr::A`... --> $DIR/defaults-cyclic-fail.rs:6:5 | @@ -26,16 +16,6 @@ note: ...which requires simplifying constant for the type system `Tr::B`... | LL | const B: u8 = Self::A; | ^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `Tr::B`... - --> $DIR/defaults-cyclic-fail.rs:8:5 - | -LL | const B: u8 = Self::A; - | ^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `Tr::B`... - --> $DIR/defaults-cyclic-fail.rs:8:5 - | -LL | const B: u8 = Self::A; - | ^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `Tr::B`... --> $DIR/defaults-cyclic-fail.rs:8:5 | diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr index aaa9b63c8f5fd..1ec52e019d4c9 100644 --- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr +++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr @@ -4,16 +4,6 @@ error[E0391]: cycle detected when simplifying constant for the type system `IMPL LL | const IMPL_REF_BAR: u32 = GlobalImplRef::BAR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: ...which requires simplifying constant for the type system `IMPL_REF_BAR`... - --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:1 - | -LL | const IMPL_REF_BAR: u32 = GlobalImplRef::BAR; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `IMPL_REF_BAR`... - --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:1 - | -LL | const IMPL_REF_BAR: u32 = GlobalImplRef::BAR; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `IMPL_REF_BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:1 | @@ -25,16 +15,6 @@ note: ...which requires simplifying constant for the type system `::BAR`... - --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:5 - | -LL | const BAR: u32 = IMPL_REF_BAR; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `::BAR`... - --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:5 - | -LL | const BAR: u32 = IMPL_REF_BAR; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `::BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:5 | diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr index a65832b096fbc..4a6d0c5019ec5 100644 --- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr +++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr @@ -4,16 +4,6 @@ error[E0391]: cycle detected when simplifying constant for the type system `DEFA LL | const DEFAULT_REF_BAR: u32 = ::BAR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: ...which requires simplifying constant for the type system `DEFAULT_REF_BAR`... - --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:11:1 - | -LL | const DEFAULT_REF_BAR: u32 = ::BAR; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `DEFAULT_REF_BAR`... - --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:11:1 - | -LL | const DEFAULT_REF_BAR: u32 = ::BAR; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `DEFAULT_REF_BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:11:1 | @@ -25,16 +15,6 @@ note: ...which requires simplifying constant for the type system `FooDefault::BA | LL | const BAR: u32 = DEFAULT_REF_BAR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `FooDefault::BAR`... - --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:8:5 - | -LL | const BAR: u32 = DEFAULT_REF_BAR; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `FooDefault::BAR`... - --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:8:5 - | -LL | const BAR: u32 = DEFAULT_REF_BAR; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `FooDefault::BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:8:5 | diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr index 1fea9c0dabaf4..727c326e594dc 100644 --- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr +++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr @@ -4,16 +4,6 @@ error[E0391]: cycle detected when simplifying constant for the type system `TRAI LL | const TRAIT_REF_BAR: u32 = ::BAR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: ...which requires simplifying constant for the type system `TRAIT_REF_BAR`... - --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:7:1 - | -LL | const TRAIT_REF_BAR: u32 = ::BAR; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `TRAIT_REF_BAR`... - --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:7:1 - | -LL | const TRAIT_REF_BAR: u32 = ::BAR; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `TRAIT_REF_BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:7:1 | @@ -25,16 +15,6 @@ note: ...which requires simplifying constant for the type system `::BAR`... - --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:12:5 - | -LL | const BAR: u32 = TRAIT_REF_BAR; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `::BAR`... - --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:12:5 - | -LL | const BAR: u32 = TRAIT_REF_BAR; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `::BAR`... --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:12:5 | diff --git a/src/test/ui/const-generics/const_evaluatable_checked/from-sig-fail.rs b/src/test/ui/const-generics/const_evaluatable_checked/from-sig-fail.rs deleted file mode 100644 index 87fdc63f6393e..0000000000000 --- a/src/test/ui/const-generics/const_evaluatable_checked/from-sig-fail.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![feature(const_generics, const_evaluatable_checked)] -#![allow(incomplete_features)] - -fn test() -> [u8; N - 1] { - todo!() -} - -fn main() { - test::<0>(); - //~^ ERROR failed to evaluate the given constant - //~| ERROR failed to evaluate the given constant -} diff --git a/src/test/ui/const-generics/const_evaluatable_checked/from-sig-fail.stderr b/src/test/ui/const-generics/const_evaluatable_checked/from-sig-fail.stderr deleted file mode 100644 index e94545235d115..0000000000000 --- a/src/test/ui/const-generics/const_evaluatable_checked/from-sig-fail.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: failed to evaluate the given constant - --> $DIR/from-sig-fail.rs:9:5 - | -LL | fn test() -> [u8; N - 1] { - | ----- required by this bound in `test` -... -LL | test::<0>(); - | ^^^^^^^^^ - -error: failed to evaluate the given constant - --> $DIR/from-sig-fail.rs:9:5 - | -LL | fn test() -> [u8; N - 1] { - | ----- required by this bound in `test` -... -LL | test::<0>(); - | ^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.full.stderr b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.full.stderr deleted file mode 100644 index c38c58897f305..0000000000000 --- a/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.full.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: failed to evaluate the given constant - --> $DIR/simple_fail.rs:14:5 - | -LL | fn test() -> Arr where Arr: Sized { - | ------ required by this bound in `test` -... -LL | test::<0>(); - | ^^^^^^^^^ - -error: failed to evaluate the given constant - --> $DIR/simple_fail.rs:14:5 - | -LL | test::<0>(); - | ^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs deleted file mode 100644 index f673b918fbcdd..0000000000000 --- a/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs +++ /dev/null @@ -1,17 +0,0 @@ -// revisions: full min -#![cfg_attr(full, feature(const_generics))] -#![feature(const_evaluatable_checked)] -#![allow(incomplete_features)] - -type Arr = [u8; N - 1]; -//[min]~^ ERROR generic parameters may not be used in const operations - -fn test() -> Arr where Arr: Sized { - todo!() -} - -fn main() { - test::<0>(); - //[full]~^ ERROR failed to evaluate the given constant - //[full]~| ERROR failed to evaluate the given constant -} diff --git a/src/test/ui/const-generics/defaults/default-param-wf-concrete.rs b/src/test/ui/const-generics/defaults/default-param-wf-concrete.rs index 41a52c7eb0d83..2d6d64bbf4de7 100644 --- a/src/test/ui/const-generics/defaults/default-param-wf-concrete.rs +++ b/src/test/ui/const-generics/defaults/default-param-wf-concrete.rs @@ -1,3 +1,4 @@ struct Foo; //~^ ERROR evaluation of constant value failed +//~| ERROR failed to evaluate fn main() {} diff --git a/src/test/ui/const-generics/defaults/default-param-wf-concrete.stderr b/src/test/ui/const-generics/defaults/default-param-wf-concrete.stderr index e8ebddade5c16..24ad7a1ad5a67 100644 --- a/src/test/ui/const-generics/defaults/default-param-wf-concrete.stderr +++ b/src/test/ui/const-generics/defaults/default-param-wf-concrete.stderr @@ -4,6 +4,12 @@ error[E0080]: evaluation of constant value failed LL | struct Foo; | ^^^^^^^ attempt to compute `u8::MAX + 1_u8`, which would overflow -error: aborting due to previous error +error: failed to evaluate the given constant + --> $DIR/default-param-wf-concrete.rs:1:18 + | +LL | struct Foo; + | ^ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/const-generics/defaults/wfness.rs b/src/test/ui/const-generics/defaults/wfness.rs index d366040ba3ea6..e13b07f5ba0cb 100644 --- a/src/test/ui/const-generics/defaults/wfness.rs +++ b/src/test/ui/const-generics/defaults/wfness.rs @@ -1,5 +1,6 @@ struct Ooopsies; //~^ error: evaluation of constant value failed +//~| ERROR failed to evaluate trait Trait {} impl Trait<3> for () {} diff --git a/src/test/ui/const-generics/defaults/wfness.stderr b/src/test/ui/const-generics/defaults/wfness.stderr index facf0ae19f734..a4015479ac488 100644 --- a/src/test/ui/const-generics/defaults/wfness.stderr +++ b/src/test/ui/const-generics/defaults/wfness.stderr @@ -4,8 +4,14 @@ error[E0080]: evaluation of constant value failed LL | struct Ooopsies; | ^^^^^^^^^^^ attempt to compute `u8::MAX + 1_u8`, which would overflow +error: failed to evaluate the given constant + --> $DIR/wfness.rs:1:23 + | +LL | struct Ooopsies; + | ^ + error[E0277]: the trait bound `(): Trait<2_u8>` is not satisfied - --> $DIR/wfness.rs:6:47 + --> $DIR/wfness.rs:7:47 | LL | struct WhereClause where (): Trait; | ^^^^^^^^ the trait `Trait<2_u8>` is not implemented for `()` @@ -14,7 +20,7 @@ LL | struct WhereClause where (): Trait; <() as Trait<3_u8>> error[E0277]: the trait bound `(): Trait<1_u8>` is not satisfied - --> $DIR/wfness.rs:14:13 + --> $DIR/wfness.rs:15:13 | LL | fn foo() -> DependentDefaultWfness { | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait<1_u8>` is not implemented for `()` @@ -22,12 +28,12 @@ LL | fn foo() -> DependentDefaultWfness { = help: the following implementations were found: <() as Trait<3_u8>> note: required by a bound in `WhereClause` - --> $DIR/wfness.rs:6:47 + --> $DIR/wfness.rs:7:47 | LL | struct WhereClause where (): Trait; | ^^^^^^^^ required by this bound in `WhereClause` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0080, E0277. For more information about an error, try `rustc --explain E0080`. diff --git a/src/test/ui/const-generics/generic_const_exprs/from-sig-fail.rs b/src/test/ui/const-generics/generic_const_exprs/from-sig-fail.rs index 90953145944fe..b0b8574f4a85f 100644 --- a/src/test/ui/const-generics/generic_const_exprs/from-sig-fail.rs +++ b/src/test/ui/const-generics/generic_const_exprs/from-sig-fail.rs @@ -8,4 +8,6 @@ fn test() -> [u8; N - 1] { fn main() { test::<0>(); + //~^ ERROR failed to evaluate + //~| ERROR failed to evaluate } diff --git a/src/test/ui/const-generics/generic_const_exprs/from-sig-fail.stderr b/src/test/ui/const-generics/generic_const_exprs/from-sig-fail.stderr index 31ccf97969472..c7a33b2571829 100644 --- a/src/test/ui/const-generics/generic_const_exprs/from-sig-fail.stderr +++ b/src/test/ui/const-generics/generic_const_exprs/from-sig-fail.stderr @@ -4,6 +4,30 @@ error[E0080]: evaluation of `test::<0_usize>::{constant#0}` failed LL | fn test() -> [u8; N - 1] { | ^^^^^ attempt to compute `0_usize - 1_usize`, which would overflow -error: aborting due to previous error +error: failed to evaluate the given constant + --> $DIR/from-sig-fail.rs:10:5 + | +LL | test::<0>(); + | ^^^^^^^^^ + | +note: required by a bound in `test` + --> $DIR/from-sig-fail.rs:4:35 + | +LL | fn test() -> [u8; N - 1] { + | ^^^^^ required by this bound in `test` + +error: failed to evaluate the given constant + --> $DIR/from-sig-fail.rs:10:5 + | +LL | test::<0>(); + | ^^^^^^^^^^^ + | +note: required by a bound in `test` + --> $DIR/from-sig-fail.rs:4:35 + | +LL | fn test() -> [u8; N - 1] { + | ^^^^^ required by this bound in `test` + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-80742.rs b/src/test/ui/const-generics/generic_const_exprs/issue-80742.rs index 275f69953024c..83e3f6f718372 100644 --- a/src/test/ui/const-generics/generic_const_exprs/issue-80742.rs +++ b/src/test/ui/const-generics/generic_const_exprs/issue-80742.rs @@ -29,4 +29,5 @@ where fn main() { let dst = Inline::::new(0); //~ ERROR //~^ ERROR + //~| ERROR failed to evaluate } diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-80742.stderr b/src/test/ui/const-generics/generic_const_exprs/issue-80742.stderr index 56cb11bacbe6b..ca8126e47ada1 100644 --- a/src/test/ui/const-generics/generic_const_exprs/issue-80742.stderr +++ b/src/test/ui/const-generics/generic_const_exprs/issue-80742.stderr @@ -1,17 +1,3 @@ -error[E0080]: evaluation of `Inline::::{constant#0}` failed - --> $SRC_DIR/core/src/mem/mod.rs:LL:COL - | -LL | intrinsics::size_of::() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | size_of called on unsized type `dyn Debug` - | inside `std::mem::size_of::` at $SRC_DIR/core/src/mem/mod.rs:LL:COL - | - ::: $DIR/issue-80742.rs:22:10 - | -LL | [u8; size_of::() + 1]: , - | -------------- inside `Inline::::{constant#0}` at $DIR/issue-80742.rs:22:10 - error[E0599]: the function or associated item `new` exists for struct `Inline`, but its trait bounds were not satisfied --> $DIR/issue-80742.rs:30:36 | @@ -35,6 +21,23 @@ LL | pub trait Debug { = note: the following trait bounds were not satisfied: `dyn Debug: Sized` +error[E0277]: the size for values of type `dyn Debug` cannot be known at compilation time + --> $DIR/issue-80742.rs:30:15 + | +LL | let dst = Inline::::new(0); + | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `dyn Debug` +note: required by a bound in `Inline` + --> $DIR/issue-80742.rs:12:15 + | +LL | struct Inline + | ^ required by this bound in `Inline` +help: consider relaxing the implicit `Sized` restriction + | +LL | struct Inline + | ++++++++ + error[E0080]: evaluation of `Inline::::{constant#0}` failed --> $SRC_DIR/core/src/mem/mod.rs:LL:COL | @@ -49,22 +52,20 @@ LL | intrinsics::size_of::() LL | [u8; size_of::() + 1]: , | -------------- inside `Inline::::{constant#0}` at $DIR/issue-80742.rs:14:10 -error[E0277]: the size for values of type `dyn Debug` cannot be known at compilation time +error: failed to evaluate the given constant --> $DIR/issue-80742.rs:30:15 | LL | let dst = Inline::::new(0); - | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | ^^^^^^^^^^^^^^^^^^^ | - = help: the trait `Sized` is not implemented for `dyn Debug` note: required by a bound in `Inline` - --> $DIR/issue-80742.rs:12:15 + --> $DIR/issue-80742.rs:14:10 | LL | struct Inline - | ^ required by this bound in `Inline` -help: consider relaxing the implicit `Sized` restriction - | -LL | struct Inline - | ++++++++ + | ------ required by a bound in this +LL | where +LL | [u8; size_of::() + 1]: , + | ^^^^^^^^^^^^^^^^^^ required by this bound in `Inline` error: aborting due to 4 previous errors diff --git a/src/test/ui/const-generics/generic_const_exprs/silent-selection-err.rs b/src/test/ui/const-generics/generic_const_exprs/silent-selection-err.rs deleted file mode 100644 index c292361e1cdfd..0000000000000 --- a/src/test/ui/const-generics/generic_const_exprs/silent-selection-err.rs +++ /dev/null @@ -1,43 +0,0 @@ -// run-pass -#![feature(const_generics, const_evaluatable_checked)] -#![allow(incomplete_features)] - -struct Array; - -trait Matrix { - fn do_it(&self) -> usize; -} - -impl Matrix for Array<0> { - fn do_it(&self) -> usize { - 0 - } -} - -impl Matrix for Array<1> { - fn do_it(&self) -> usize { - 1 - } -} - -impl Matrix for Array<2> { - fn do_it(&self) -> usize { - 2 - } -} - -impl Matrix for Array -where - [u8; N - 3]: Sized, -{ - fn do_it(&self) -> usize { - N + 1 - } -} - -fn main() { - assert_eq!(Array::<0>.do_it(), 0); - assert_eq!(Array::<1>.do_it(), 1); - assert_eq!(Array::<2>.do_it(), 2); - assert_eq!(Array::<3>.do_it(), 4); -} diff --git a/src/test/ui/const-generics/generic_const_exprs/simple_fail.rs b/src/test/ui/const-generics/generic_const_exprs/simple_fail.rs index c47a350c7fb43..6a78a6b23cf23 100644 --- a/src/test/ui/const-generics/generic_const_exprs/simple_fail.rs +++ b/src/test/ui/const-generics/generic_const_exprs/simple_fail.rs @@ -11,4 +11,6 @@ fn test() -> Arr where [u8; N - 1]: Sized { fn main() { test::<0>(); + //~^ ERROR failed to evaluate + //~| ERROR failed to evaluate } diff --git a/src/test/ui/const-generics/generic_const_exprs/simple_fail.stderr b/src/test/ui/const-generics/generic_const_exprs/simple_fail.stderr index 99fc92fb4f0ad..e3b493c4c8833 100644 --- a/src/test/ui/const-generics/generic_const_exprs/simple_fail.stderr +++ b/src/test/ui/const-generics/generic_const_exprs/simple_fail.stderr @@ -4,12 +4,30 @@ error[E0080]: evaluation of `test::<0_usize>::{constant#0}` failed LL | fn test() -> Arr where [u8; N - 1]: Sized { | ^^^^^ attempt to compute `0_usize - 1_usize`, which would overflow +error: failed to evaluate the given constant + --> $DIR/simple_fail.rs:13:5 + | +LL | test::<0>(); + | ^^^^^^^^^ + | +note: required by a bound in `test` + --> $DIR/simple_fail.rs:7:48 + | +LL | fn test() -> Arr where [u8; N - 1]: Sized { + | ^^^^^ required by this bound in `test` + error[E0080]: evaluation of `Arr::<0_usize>::{constant#0}` failed --> $DIR/simple_fail.rs:4:33 | LL | type Arr = [u8; N - 1]; | ^^^^^ attempt to compute `0_usize - 1_usize`, which would overflow -error: aborting due to 2 previous errors +error: failed to evaluate the given constant + --> $DIR/simple_fail.rs:13:5 + | +LL | test::<0>(); + | ^^^^^^^^^^^ + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/const-generics/issues/issue-67739.rs b/src/test/ui/const-generics/issues/issue-67739.rs index de0eb7f509ae5..8eae84c8bfb30 100644 --- a/src/test/ui/const-generics/issues/issue-67739.rs +++ b/src/test/ui/const-generics/issues/issue-67739.rs @@ -9,8 +9,8 @@ pub trait Trait { fn associated_size(&self) -> usize { [0u8; mem::size_of::()]; - //[min]~^ ERROR constant expression depends on a generic parameter - //[full]~^^ ERROR unconstrained generic constant + //[full]~^ ERROR unconstrained generic constant + //[min]~^^ ERROR constant expression depends on a generic parameter 0 } } diff --git a/src/test/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr b/src/test/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr index 415a53a56274a..f61067fb8fdd5 100644 --- a/src/test/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr +++ b/src/test/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr @@ -34,7 +34,24 @@ LL | get_flag::(); } error[E0080]: it is undefined behavior to use this value - --> $DIR/invalid-patterns.rs:40:14 + --> $DIR/invalid-patterns.rs:38:21 + | +LL | get_flag::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + __ __ __ __ │ ░░░░ + } + +error: failed to evaluate the given constant + --> $DIR/invalid-patterns.rs:38:21 + | +LL | get_flag::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0080]: it is undefined behavior to use this value + --> $DIR/invalid-patterns.rs:42:14 | LL | get_flag::<{ unsafe { bool_raw.boolean } }, 'z'>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x42, but expected a boolean @@ -47,6 +64,45 @@ LL | get_flag::<{ unsafe { bool_raw.boolean } }, 'z'>(); error[E0080]: it is undefined behavior to use this value --> $DIR/invalid-patterns.rs:42:14 | +LL | get_flag::<{ unsafe { bool_raw.boolean } }, 'z'>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x42, but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + 42 │ B + } + +error: failed to evaluate the given constant + --> $DIR/invalid-patterns.rs:42:14 + | +LL | get_flag::<{ unsafe { bool_raw.boolean } }, 'z'>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0080]: it is undefined behavior to use this value + --> $DIR/invalid-patterns.rs:46:14 + | +LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x42, but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + 42 │ B + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/invalid-patterns.rs:46:47 + | +LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + __ __ __ __ │ ░░░░ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/invalid-patterns.rs:46:14 + | LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x42, but expected a boolean | @@ -55,8 +111,14 @@ LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character 42 │ B } +error: failed to evaluate the given constant + --> $DIR/invalid-patterns.rs:46:14 + | +LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0080]: it is undefined behavior to use this value - --> $DIR/invalid-patterns.rs:42:47 + --> $DIR/invalid-patterns.rs:46:47 | LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) @@ -66,7 +128,13 @@ LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character __ __ __ __ │ ░░░░ } -error: aborting due to 8 previous errors +error: failed to evaluate the given constant + --> $DIR/invalid-patterns.rs:46:47 + | +LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 16 previous errors Some errors have detailed explanations: E0080, E0308. For more information about an error, try `rustc --explain E0080`. diff --git a/src/test/ui/const-generics/min_const_generics/invalid-patterns.rs b/src/test/ui/const-generics/min_const_generics/invalid-patterns.rs index 682e0eced9dff..d4e4abed44f57 100644 --- a/src/test/ui/const-generics/min_const_generics/invalid-patterns.rs +++ b/src/test/ui/const-generics/min_const_generics/invalid-patterns.rs @@ -37,9 +37,17 @@ fn main() { get_flag::(); //~^ ERROR it is undefined behavior + //~| ERROR it is undefined behavior + //~| ERROR failed to evaluate get_flag::<{ unsafe { bool_raw.boolean } }, 'z'>(); //~^ ERROR it is undefined behavior + //~| ERROR it is undefined behavior + //~| ERROR failed to evaluate get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); //~^ ERROR it is undefined behavior //~| ERROR it is undefined behavior + //~| ERROR it is undefined behavior + //~| ERROR it is undefined behavior + //~| ERROR failed to evaluate + //~| ERROR failed to evaluate } diff --git a/src/test/ui/consts/const-eval/const-eval-overflow-3.rs b/src/test/ui/consts/const-eval/const-eval-overflow-3.rs index 89e0011b7d10c..334016f76f8cd 100644 --- a/src/test/ui/consts/const-eval/const-eval-overflow-3.rs +++ b/src/test/ui/consts/const-eval/const-eval-overflow-3.rs @@ -17,7 +17,8 @@ const A_I8_I : [u32; (i8::MAX as usize) + 1] = [0; (i8::MAX + 1) as usize]; //~^ ERROR evaluation of constant value failed -//~| ERROR failed to evaluate the given constant +//~| ERROR evaluation of constant value failed +//~| ERROR failed to evaluate fn main() { foo(&A_I8_I[..]); diff --git a/src/test/ui/consts/const-eval/const-eval-overflow-3.stderr b/src/test/ui/consts/const-eval/const-eval-overflow-3.stderr index 2d1cd5459125b..d1f103a145198 100644 --- a/src/test/ui/consts/const-eval/const-eval-overflow-3.stderr +++ b/src/test/ui/consts/const-eval/const-eval-overflow-3.stderr @@ -4,12 +4,18 @@ error[E0080]: evaluation of constant value failed LL | = [0; (i8::MAX + 1) as usize]; | ^^^^^^^^^^^^^ attempt to compute `i8::MAX + 1_i8`, which would overflow +error[E0080]: evaluation of constant value failed + --> $DIR/const-eval-overflow-3.rs:18:11 + | +LL | = [0; (i8::MAX + 1) as usize]; + | ^^^^^^^^^^^^^ attempt to compute `i8::MAX + 1_i8`, which would overflow + error: failed to evaluate the given constant --> $DIR/const-eval-overflow-3.rs:18:11 | LL | = [0; (i8::MAX + 1) as usize]; | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/infinite_loop.rs b/src/test/ui/consts/const-eval/infinite_loop.rs index 3c40c337cd005..08cf043e867ab 100644 --- a/src/test/ui/consts/const-eval/infinite_loop.rs +++ b/src/test/ui/consts/const-eval/infinite_loop.rs @@ -6,6 +6,7 @@ fn main() { while n != 0 { n = if n % 2 == 0 { n/2 } else { 3*n + 1 }; //~^ ERROR evaluation of constant value failed + //~| ERROR evaluation of constant value failed } n }]; diff --git a/src/test/ui/consts/const-eval/infinite_loop.stderr b/src/test/ui/consts/const-eval/infinite_loop.stderr index b30a53619b574..9770435678984 100644 --- a/src/test/ui/consts/const-eval/infinite_loop.stderr +++ b/src/test/ui/consts/const-eval/infinite_loop.stderr @@ -1,3 +1,9 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/infinite_loop.rs:7:20 + | +LL | n = if n % 2 == 0 { n/2 } else { 3*n + 1 }; + | ^^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`) + error: failed to evaluate the given constant --> $DIR/infinite_loop.rs:4:18 | @@ -17,6 +23,6 @@ error[E0080]: evaluation of constant value failed LL | n = if n % 2 == 0 { n/2 } else { 3*n + 1 }; | ^^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`) -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/issue-49296.stderr b/src/test/ui/consts/const-eval/issue-49296.stderr index 5fc869d4ce137..bc3074b10bee6 100644 --- a/src/test/ui/consts/const-eval/issue-49296.stderr +++ b/src/test/ui/consts/const-eval/issue-49296.stderr @@ -2,15 +2,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/issue-49296.rs:11:16 | LL | const X: u64 = *wat(42); -<<<<<<< HEAD | ^^^^^^^^ pointer to alloc2 was dereferenced after this allocation got freed -======= - | ---------------^^^^^^^^- - | | - | pointer to alloc4 was dereferenced after this allocation got freed - | - = note: `#[deny(const_err)]` on by default ->>>>>>> 9ee0d801b76 (update tests) error: aborting due to previous error diff --git a/src/test/ui/consts/const-eval/issue-52475.rs b/src/test/ui/consts/const-eval/issue-52475.rs index 6b8b42d417b35..fe5d52e4b1e49 100644 --- a/src/test/ui/consts/const-eval/issue-52475.rs +++ b/src/test/ui/consts/const-eval/issue-52475.rs @@ -3,7 +3,9 @@ fn main() { let mut x = &0; let mut n = 0; while n < 5 { - n = (n + 1) % 5; //~ ERROR evaluation of constant value failed + n = (n + 1) % 5; + //~^ ERROR evaluation of constant value failed + //~| ERROR evaluation of constant value failed x = &0; // Materialize a new AllocId } 0 diff --git a/src/test/ui/consts/const-eval/issue-52475.stderr b/src/test/ui/consts/const-eval/issue-52475.stderr index 5679f51c9ea54..aa09c2925c1f3 100644 --- a/src/test/ui/consts/const-eval/issue-52475.stderr +++ b/src/test/ui/consts/const-eval/issue-52475.stderr @@ -1,3 +1,9 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/issue-52475.rs:6:17 + | +LL | n = (n + 1) % 5; + | ^^^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`) + error: failed to evaluate the given constant --> $DIR/issue-52475.rs:2:18 | @@ -17,6 +23,6 @@ error[E0080]: evaluation of constant value failed LL | n = (n + 1) % 5; | ^^^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`) -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-enum.64bit.stderr b/src/test/ui/consts/const-eval/ub-enum.64bit.stderr index 4f7dd5cdf7c73..0d7d1f691ed0e 100644 --- a/src/test/ui/consts/const-eval/ub-enum.64bit.stderr +++ b/src/test/ui/consts/const-eval/ub-enum.64bit.stderr @@ -13,22 +13,22 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:27:1 | LL | const BAD_ENUM_PTR: Enum = unsafe { mem::transmute(&1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .: encountered pointer to alloc9, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .: encountered pointer to alloc11, but expected initialized plain (non-pointer) bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾───────alloc9────────╼ │ ╾──────╼ + ╾───────alloc11───────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:30:1 | LL | const BAD_ENUM_WRAPPED: Wrap = unsafe { mem::transmute(&1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0.: encountered pointer to alloc13, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0.: encountered pointer to alloc15, but expected initialized plain (non-pointer) bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾───────alloc13───────╼ │ ╾──────╼ + ╾───────alloc15───────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value @@ -46,22 +46,22 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:44:1 | LL | const BAD_ENUM2_PTR: Enum2 = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .: encountered pointer to alloc19, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .: encountered pointer to alloc21, but expected initialized plain (non-pointer) bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾───────alloc19───────╼ │ ╾──────╼ + ╾───────alloc21───────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:47:1 | LL | const BAD_ENUM2_WRAPPED: Wrap = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0.: encountered pointer to alloc23, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0.: encountered pointer to alloc25, but expected initialized plain (non-pointer) bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾───────alloc23───────╼ │ ╾──────╼ + ╾───────alloc25───────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value @@ -79,11 +79,11 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:60:1 | LL | const BAD_ENUM2_OPTION_PTR: Option = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .: encountered pointer to alloc29, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .: encountered pointer to alloc31, but expected initialized plain (non-pointer) bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾───────alloc29───────╼ │ ╾──────╼ + ╾───────alloc31───────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value diff --git a/src/test/ui/consts/const-size_of-cycle.stderr b/src/test/ui/consts/const-size_of-cycle.stderr index 0b1b6f0caf192..82fed16e06ca6 100644 --- a/src/test/ui/consts/const-size_of-cycle.stderr +++ b/src/test/ui/consts/const-size_of-cycle.stderr @@ -4,16 +4,6 @@ error[E0391]: cycle detected when simplifying constant for the type system `Foo: LL | bytes: [u8; std::mem::size_of::()] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: ...which requires simplifying constant for the type system `Foo::bytes::{constant#0}`... - --> $DIR/const-size_of-cycle.rs:4:17 - | -LL | bytes: [u8; std::mem::size_of::()] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `Foo::bytes::{constant#0}`... - --> $DIR/const-size_of-cycle.rs:4:17 - | -LL | bytes: [u8; std::mem::size_of::()] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`... --> $DIR/const-size_of-cycle.rs:4:17 | diff --git a/src/test/ui/consts/issue-36163.stderr b/src/test/ui/consts/issue-36163.stderr index 96ab7033e2501..915cf4fafd7bd 100644 --- a/src/test/ui/consts/issue-36163.stderr +++ b/src/test/ui/consts/issue-36163.stderr @@ -4,16 +4,6 @@ error[E0391]: cycle detected when simplifying constant for the type system `Foo: LL | B = A, | ^ | -note: ...which requires simplifying constant for the type system `Foo::B::{constant#0}`... - --> $DIR/issue-36163.rs:4:9 - | -LL | B = A, - | ^ -note: ...which requires simplifying constant for the type system `Foo::B::{constant#0}`... - --> $DIR/issue-36163.rs:4:9 - | -LL | B = A, - | ^ note: ...which requires const-evaluating + checking `Foo::B::{constant#0}`... --> $DIR/issue-36163.rs:4:9 | @@ -25,16 +15,6 @@ note: ...which requires simplifying constant for the type system `A`... | LL | const A: isize = Foo::B as isize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `A`... - --> $DIR/issue-36163.rs:1:1 - | -LL | const A: isize = Foo::B as isize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `A`... - --> $DIR/issue-36163.rs:1:1 - | -LL | const A: isize = Foo::B as isize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `A`... --> $DIR/issue-36163.rs:1:1 | diff --git a/src/test/ui/consts/issue-44415.stderr b/src/test/ui/consts/issue-44415.stderr index 6a8e09cfcb971..4a9df8206ded1 100644 --- a/src/test/ui/consts/issue-44415.stderr +++ b/src/test/ui/consts/issue-44415.stderr @@ -4,16 +4,6 @@ error[E0391]: cycle detected when simplifying constant for the type system `Foo: LL | bytes: [u8; unsafe { intrinsics::size_of::() }], | ^^^^^^ | -note: ...which requires simplifying constant for the type system `Foo::bytes::{constant#0}`... - --> $DIR/issue-44415.rs:6:17 - | -LL | bytes: [u8; unsafe { intrinsics::size_of::() }], - | ^^^^^^ -note: ...which requires simplifying constant for the type system `Foo::bytes::{constant#0}`... - --> $DIR/issue-44415.rs:6:17 - | -LL | bytes: [u8; unsafe { intrinsics::size_of::() }], - | ^^^^^^ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`... --> $DIR/issue-44415.rs:6:17 | diff --git a/src/test/ui/consts/issue-52432.rs b/src/test/ui/consts/issue-52432.rs deleted file mode 100644 index 6be782f95ad37..0000000000000 --- a/src/test/ui/consts/issue-52432.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![feature(const_raw_ptr_to_usize_cast)] - -fn main() { - [(); &(static |x| {}) as *const _ as usize]; - //~^ ERROR: closures cannot be static - //~| ERROR: type annotations needed - [(); &(static || {}) as *const _ as usize]; - //~^ ERROR: closures cannot be static - //~| ERROR: failed to evaluate the given constant -} diff --git a/src/test/ui/consts/issue-52432.stderr b/src/test/ui/consts/issue-52432.stderr deleted file mode 100644 index 2710a72f3a105..0000000000000 --- a/src/test/ui/consts/issue-52432.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error[E0697]: closures cannot be static - --> $DIR/issue-52432.rs:4:12 - | -LL | [(); &(static |x| {}) as *const _ as usize]; - | ^^^^^^^^^^ - -error[E0697]: closures cannot be static - --> $DIR/issue-52432.rs:7:12 - | -LL | [(); &(static || {}) as *const _ as usize]; - | ^^^^^^^^^ - -error[E0282]: type annotations needed - --> $DIR/issue-52432.rs:4:20 - | -LL | [(); &(static |x| {}) as *const _ as usize]; - | ^ consider giving this closure parameter a type - -error: failed to evaluate the given constant - --> $DIR/issue-52432.rs:7:10 - | -LL | [(); &(static || {}) as *const _ as usize]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 4 previous errors - -Some errors have detailed explanations: E0282, E0697. -For more information about an error, try `rustc --explain E0282`. diff --git a/src/test/ui/consts/issue-66693-panic-in-array-len.rs b/src/test/ui/consts/issue-66693-panic-in-array-len.rs index fc0dcd7a44daa..b929bd3f351cf 100644 --- a/src/test/ui/consts/issue-66693-panic-in-array-len.rs +++ b/src/test/ui/consts/issue-66693-panic-in-array-len.rs @@ -9,6 +9,8 @@ fn main() { // ensure that conforming panics are handled correctly let _ = [false; panic!()]; //~^ ERROR: evaluation of constant value failed + //~| ERROR: evaluation of constant value failed + //~| ERROR failed to evaluate the given constant // typechecking halts before getting to this one let _ = ['a', panic!("panic in array len")]; diff --git a/src/test/ui/consts/issue-66693-panic-in-array-len.stderr b/src/test/ui/consts/issue-66693-panic-in-array-len.stderr index 4ccbeb73c8a25..1ff69c1faef0f 100644 --- a/src/test/ui/consts/issue-66693-panic-in-array-len.stderr +++ b/src/test/ui/consts/issue-66693-panic-in-array-len.stderr @@ -14,6 +14,22 @@ LL | let _ = [false; panic!()]; | = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 2 previous errors +error: failed to evaluate the given constant + --> $DIR/issue-66693-panic-in-array-len.rs:10:21 + | +LL | let _ = [false; panic!()]; + | ^^^^^^^^ + | + = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: evaluation of constant value failed + --> $DIR/issue-66693-panic-in-array-len.rs:10:21 + | +LL | let _ = [false; panic!()]; + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/issue-66693-panic-in-array-len.rs:10:21 + | + = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/recursive-zst-static.default.stderr b/src/test/ui/consts/recursive-zst-static.default.stderr index 8ddc9fe4f5ff9..8d2cfd284e20f 100644 --- a/src/test/ui/consts/recursive-zst-static.default.stderr +++ b/src/test/ui/consts/recursive-zst-static.default.stderr @@ -4,17 +4,7 @@ error[E0391]: cycle detected when const-evaluating + checking `FOO` LL | static FOO: () = FOO; | ^^^^^^^^^^^^^^^^^^^^^ | -note: ...which requires const-evaluating + checking `FOO`... - --> $DIR/recursive-zst-static.rs:10:1 - | -LL | static FOO: () = FOO; - | ^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires const-evaluating + checking `FOO`... - --> $DIR/recursive-zst-static.rs:10:1 - | -LL | static FOO: () = FOO; - | ^^^^^^^^^^^^^^^^^^^^^ - = note: ...which again requires const-evaluating + checking `FOO`, completing the cycle + = note: ...which immediately requires const-evaluating + checking `FOO` again = note: cycle used when running analysis passes on this crate error: aborting due to previous error diff --git a/src/test/ui/consts/recursive-zst-static.unleash.stderr b/src/test/ui/consts/recursive-zst-static.unleash.stderr index 8ddc9fe4f5ff9..8d2cfd284e20f 100644 --- a/src/test/ui/consts/recursive-zst-static.unleash.stderr +++ b/src/test/ui/consts/recursive-zst-static.unleash.stderr @@ -4,17 +4,7 @@ error[E0391]: cycle detected when const-evaluating + checking `FOO` LL | static FOO: () = FOO; | ^^^^^^^^^^^^^^^^^^^^^ | -note: ...which requires const-evaluating + checking `FOO`... - --> $DIR/recursive-zst-static.rs:10:1 - | -LL | static FOO: () = FOO; - | ^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires const-evaluating + checking `FOO`... - --> $DIR/recursive-zst-static.rs:10:1 - | -LL | static FOO: () = FOO; - | ^^^^^^^^^^^^^^^^^^^^^ - = note: ...which again requires const-evaluating + checking `FOO`, completing the cycle + = note: ...which immediately requires const-evaluating + checking `FOO` again = note: cycle used when running analysis passes on this crate error: aborting due to previous error diff --git a/src/test/ui/consts/write-to-static-mut-in-static.stderr b/src/test/ui/consts/write-to-static-mut-in-static.stderr index 6493ddabc4ea0..8916ea3283565 100644 --- a/src/test/ui/consts/write-to-static-mut-in-static.stderr +++ b/src/test/ui/consts/write-to-static-mut-in-static.stderr @@ -10,17 +10,7 @@ error[E0391]: cycle detected when const-evaluating + checking `C` LL | pub static mut C: u32 = unsafe { C = 1; 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: ...which requires const-evaluating + checking `C`... - --> $DIR/write-to-static-mut-in-static.rs:5:1 - | -LL | pub static mut C: u32 = unsafe { C = 1; 0 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires const-evaluating + checking `C`... - --> $DIR/write-to-static-mut-in-static.rs:5:1 - | -LL | pub static mut C: u32 = unsafe { C = 1; 0 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which again requires const-evaluating + checking `C`, completing the cycle + = note: ...which immediately requires const-evaluating + checking `C` again = note: cycle used when running analysis passes on this crate error: aborting due to 2 previous errors diff --git a/src/test/ui/enum/issue-67945-1.rs b/src/test/ui/enum/issue-67945-1.rs index f4697344cc7a4..e279dd1e501ca 100644 --- a/src/test/ui/enum/issue-67945-1.rs +++ b/src/test/ui/enum/issue-67945-1.rs @@ -1,6 +1,7 @@ enum Bug { //~ ERROR parameter `S` is never used Var = { - let x: S = 0; //~ ERROR generic parameters may not be used + let x: S = 0; + //~^ ERROR generic parameters may not be used 0 }, } diff --git a/src/test/ui/issues/issue-17252.stderr b/src/test/ui/issues/issue-17252.stderr index 2372bbb5ca3d7..86e81a0784830 100644 --- a/src/test/ui/issues/issue-17252.stderr +++ b/src/test/ui/issues/issue-17252.stderr @@ -5,16 +5,6 @@ note: ...which requires simplifying constant for the type system `FOO`... | LL | const FOO: usize = FOO; | ^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `FOO`... - --> $DIR/issue-17252.rs:1:1 - | -LL | const FOO: usize = FOO; - | ^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `FOO`... - --> $DIR/issue-17252.rs:1:1 - | -LL | const FOO: usize = FOO; - | ^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `FOO`... --> $DIR/issue-17252.rs:1:1 | diff --git a/src/test/ui/issues/issue-23302-1.stderr b/src/test/ui/issues/issue-23302-1.stderr index 4b5a33057f50d..b71d07f10ea9c 100644 --- a/src/test/ui/issues/issue-23302-1.stderr +++ b/src/test/ui/issues/issue-23302-1.stderr @@ -4,16 +4,6 @@ error[E0391]: cycle detected when simplifying constant for the type system `X::A LL | A = X::A as isize, | ^^^^^^^^^^^^^ | -note: ...which requires simplifying constant for the type system `X::A::{constant#0}`... - --> $DIR/issue-23302-1.rs:4:9 - | -LL | A = X::A as isize, - | ^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `X::A::{constant#0}`... - --> $DIR/issue-23302-1.rs:4:9 - | -LL | A = X::A as isize, - | ^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `X::A::{constant#0}`... --> $DIR/issue-23302-1.rs:4:9 | diff --git a/src/test/ui/issues/issue-23302-2.stderr b/src/test/ui/issues/issue-23302-2.stderr index e2a0d977cc655..94c68b6ae5e14 100644 --- a/src/test/ui/issues/issue-23302-2.stderr +++ b/src/test/ui/issues/issue-23302-2.stderr @@ -4,16 +4,6 @@ error[E0391]: cycle detected when simplifying constant for the type system `Y::A LL | A = Y::B as isize, | ^^^^^^^^^^^^^ | -note: ...which requires simplifying constant for the type system `Y::A::{constant#0}`... - --> $DIR/issue-23302-2.rs:4:9 - | -LL | A = Y::B as isize, - | ^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `Y::A::{constant#0}`... - --> $DIR/issue-23302-2.rs:4:9 - | -LL | A = Y::B as isize, - | ^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `Y::A::{constant#0}`... --> $DIR/issue-23302-2.rs:4:9 | diff --git a/src/test/ui/issues/issue-23302-3.stderr b/src/test/ui/issues/issue-23302-3.stderr index 66ba9c2f68e7c..441fcdcea04c4 100644 --- a/src/test/ui/issues/issue-23302-3.stderr +++ b/src/test/ui/issues/issue-23302-3.stderr @@ -4,16 +4,6 @@ error[E0391]: cycle detected when simplifying constant for the type system `A` LL | const A: i32 = B; | ^^^^^^^^^^^^^^^^^ | -note: ...which requires simplifying constant for the type system `A`... - --> $DIR/issue-23302-3.rs:1:1 - | -LL | const A: i32 = B; - | ^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `A`... - --> $DIR/issue-23302-3.rs:1:1 - | -LL | const A: i32 = B; - | ^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `A`... --> $DIR/issue-23302-3.rs:1:1 | @@ -25,16 +15,6 @@ note: ...which requires simplifying constant for the type system `B`... | LL | const B: i32 = A; | ^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `B`... - --> $DIR/issue-23302-3.rs:3:1 - | -LL | const B: i32 = A; - | ^^^^^^^^^^^^^^^^^ -note: ...which requires simplifying constant for the type system `B`... - --> $DIR/issue-23302-3.rs:3:1 - | -LL | const B: i32 = A; - | ^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `B`... --> $DIR/issue-23302-3.rs:3:1 | diff --git a/src/test/ui/mir/issue-80742.rs b/src/test/ui/mir/issue-80742.rs index d82f73fc2a189..bf7d4e9ab3131 100644 --- a/src/test/ui/mir/issue-80742.rs +++ b/src/test/ui/mir/issue-80742.rs @@ -3,8 +3,7 @@ // This test used to cause an ICE in rustc_mir::interpret::step::eval_rvalue_into_place #![allow(incomplete_features)] -#![feature(const_evaluatable_checked)] -#![feature(const_generics)] +#![feature(generic_const_exprs)] use std::fmt::Debug; use std::marker::PhantomData; @@ -29,7 +28,7 @@ where fn main() { let dst = Inline::::new(0); - //~^ ERROR failed to evaluate the given constant + //~^ ERROR the function or associated item `new` exists for struct //~| ERROR the size for values of type - //~| ERROR no function or associated item + //~| ERROR failed to evaluate } diff --git a/src/test/ui/mir/issue-80742.stderr b/src/test/ui/mir/issue-80742.stderr index 61992f3b9c407..ca8126e47ada1 100644 --- a/src/test/ui/mir/issue-80742.stderr +++ b/src/test/ui/mir/issue-80742.stderr @@ -1,5 +1,5 @@ -error[E0599]: no function or associated item named `new` found for struct `Inline` in the current scope - --> $DIR/issue-80742.rs:31:36 +error[E0599]: the function or associated item `new` exists for struct `Inline`, but its trait bounds were not satisfied + --> $DIR/issue-80742.rs:30:36 | LL | / struct Inline LL | | where @@ -11,44 +11,63 @@ LL | | } | |_- function or associated item `new` not found for this ... LL | let dst = Inline::::new(0); - | ^^^ function or associated item not found in `Inline` - | + | ^^^ function or associated item cannot be called on `Inline` due to unsatisfied trait bounds + | ::: $SRC_DIR/core/src/fmt/mod.rs:LL:COL | LL | pub trait Debug { | --------------- doesn't satisfy `dyn Debug: Sized` | - = note: the method `new` exists but the following trait bounds were not satisfied: + = note: the following trait bounds were not satisfied: `dyn Debug: Sized` error[E0277]: the size for values of type `dyn Debug` cannot be known at compilation time - --> $DIR/issue-80742.rs:31:15 + --> $DIR/issue-80742.rs:30:15 | -LL | struct Inline - | - required by this bound in `Inline` -... LL | let dst = Inline::::new(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `dyn Debug` +note: required by a bound in `Inline` + --> $DIR/issue-80742.rs:12:15 + | +LL | struct Inline + | ^ required by this bound in `Inline` help: consider relaxing the implicit `Sized` restriction | LL | struct Inline - | ^^^^^^^^ + | ++++++++ + +error[E0080]: evaluation of `Inline::::{constant#0}` failed + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + | +LL | intrinsics::size_of::() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | size_of called on unsized type `dyn Debug` + | inside `std::mem::size_of::` at $SRC_DIR/core/src/mem/mod.rs:LL:COL + | + ::: $DIR/issue-80742.rs:14:10 + | +LL | [u8; size_of::() + 1]: , + | -------------- inside `Inline::::{constant#0}` at $DIR/issue-80742.rs:14:10 error: failed to evaluate the given constant - --> $DIR/issue-80742.rs:31:15 + --> $DIR/issue-80742.rs:30:15 + | +LL | let dst = Inline::::new(0); + | ^^^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `Inline` + --> $DIR/issue-80742.rs:14:10 | LL | struct Inline | ------ required by a bound in this LL | where LL | [u8; size_of::() + 1]: , - | ------------------ required by this bound in `Inline` -... -LL | let dst = Inline::::new(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ required by this bound in `Inline` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0277, E0599. -For more information about an error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0080, E0277, E0599. +For more information about an error, try `rustc --explain E0080`. diff --git a/src/test/ui/recursion/recursive-static-definition.stderr b/src/test/ui/recursion/recursive-static-definition.stderr index 42bfa76000b1e..56767f391531b 100644 --- a/src/test/ui/recursion/recursive-static-definition.stderr +++ b/src/test/ui/recursion/recursive-static-definition.stderr @@ -4,17 +4,7 @@ error[E0391]: cycle detected when const-evaluating + checking `FOO` LL | pub static FOO: u32 = FOO; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: ...which requires const-evaluating + checking `FOO`... - --> $DIR/recursive-static-definition.rs:1:1 - | -LL | pub static FOO: u32 = FOO; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires const-evaluating + checking `FOO`... - --> $DIR/recursive-static-definition.rs:1:1 - | -LL | pub static FOO: u32 = FOO; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which again requires const-evaluating + checking `FOO`, completing the cycle + = note: ...which immediately requires const-evaluating + checking `FOO` again = note: cycle used when running analysis passes on this crate error: aborting due to previous error diff --git a/src/test/ui/treat-err-as-bug/err.stderr b/src/test/ui/treat-err-as-bug/err.stderr index 8f67571c2990e..40dc0a8685b20 100644 --- a/src/test/ui/treat-err-as-bug/err.stderr +++ b/src/test/ui/treat-err-as-bug/err.stderr @@ -8,5 +8,5 @@ error: internal compiler error: unexpected panic query stack during panic: #0 [eval_to_allocation_raw] const-evaluating + checking `C` -#1 [eval_to_allocation_raw] const-evaluating + checking `C` +#1 [analysis] running analysis passes on this crate end of query stack diff --git a/src/test/ui/type-alias-enum-variants/self-in-enum-definition.stderr b/src/test/ui/type-alias-enum-variants/self-in-enum-definition.stderr index 211086e5ec528..c0f67b2889a99 100644 --- a/src/test/ui/type-alias-enum-variants/self-in-enum-definition.stderr +++ b/src/test/ui/type-alias-enum-variants/self-in-enum-definition.stderr @@ -4,16 +4,6 @@ error[E0391]: cycle detected when simplifying constant for the type system `Alph LL | V3 = Self::V1 {} as u8 + 2, | ^^^^^^^^ | -note: ...which requires simplifying constant for the type system `Alpha::V3::{constant#0}`... - --> $DIR/self-in-enum-definition.rs:5:10 - | -LL | V3 = Self::V1 {} as u8 + 2, - | ^^^^^^^^ -note: ...which requires simplifying constant for the type system `Alpha::V3::{constant#0}`... - --> $DIR/self-in-enum-definition.rs:5:10 - | -LL | V3 = Self::V1 {} as u8 + 2, - | ^^^^^^^^ note: ...which requires const-evaluating + checking `Alpha::V3::{constant#0}`... --> $DIR/self-in-enum-definition.rs:5:10 | From 78e4b6f98d217c13f6d9b45d7956192052b917c5 Mon Sep 17 00:00:00 2001 From: b-naber Date: Fri, 21 Jan 2022 09:46:13 +0100 Subject: [PATCH 10/11] simplify deduplication and better utilize query caching --- .../src/const_eval/eval_queries.rs | 22 +-- .../src/interpret/eval_context.rs | 2 +- .../rustc_middle/src/mir/interpret/mod.rs | 163 ++++++++++++------ 3 files changed, 118 insertions(+), 69 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 59d448359ad52..7a201c53b1e98 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -10,7 +10,7 @@ use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_middle::mir; -use rustc_middle::mir::interpret::{ConstDedupResult, ErrorHandled}; +use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::mir::pretty::display_allocation; use rustc_middle::traits::Reveal; use rustc_middle::ty::layout::{LayoutError, LayoutOf}; @@ -235,8 +235,7 @@ pub fn eval_to_const_value_raw_provider<'tcx>( match eval_nullary_intrinsic(tcx, param_env, def_id, substs) { Ok(val) => { // store result for deduplication - let res = ConstDedupResult::new(reveal, val); - tcx.save_const_value_for_dedup(id, res); + tcx.save_const_value_for_dedup(id, reveal); return Ok(val); } @@ -251,17 +250,7 @@ pub fn eval_to_const_value_raw_provider<'tcx>( } } - let result = - tcx.dedup_eval_alloc_raw(key, None).map(|val| turn_into_const_value(tcx, val, key)); - - match result { - Ok(val) => { - tcx.save_const_value_for_dedup(id, ConstDedupResult::new(reveal, val)); - } - _ => {} - } - - result + tcx.dedup_eval_alloc_raw(key, None).map(|val| turn_into_const_value(tcx, val, key)) } #[instrument(skip(tcx), level = "debug")] @@ -431,10 +420,9 @@ pub fn eval_to_allocation_raw_provider<'tcx>( } else { // Convert to raw constant let const_alloc = ConstAlloc { alloc_id, ty: mplace.layout.ty }; - let val = ConstDedupResult::new(reveal, const_alloc); - // store result in order to deduplicate later - tcx.save_alloc_for_dedup(cid, val); + // store information that allows deduplication + tcx.save_alloc_for_dedup(cid, reveal); Ok(const_alloc) } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 8cfe5da763515..f73d2dff92029 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -810,7 +810,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// cause us to continue unwinding. pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> InterpResult<'tcx> { info!( - "popping stack frame ({})", + "popping following stack frame ({})", if unwinding { "during unwinding" } else { "returning from function" } ); diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 67e50420e8240..cda1d7e1ca7c7 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -485,32 +485,15 @@ impl<'tcx> fmt::Display for FrameInfo<'tcx> { } } -#[derive(Clone, Copy, Debug)] -pub enum ConstDedupResult { - Selection(T), - UserFacing(T), - All(T), -} - -impl ConstDedupResult { - pub fn new(reveal: Reveal, val: T) -> Self { - match reveal { - Reveal::Selection => ConstDedupResult::Selection(val), - Reveal::UserFacing => ConstDedupResult::UserFacing(val), - Reveal::All => ConstDedupResult::All(val), - } - } -} - /// Used to store results of calls to `eval_to_allocation_raw` and /// `eval_to_const_value_raw`. #[derive(Debug)] pub struct ConstDedupMap<'tcx> { // interning for deduplication of `eval_to_allocation_raw` - pub alloc_map: RefCell, ConstDedupResult>>>, + pub alloc_map: RefCell, Reveal>>, // interning for deduplication of `eval_to_const_value_raw` - pub const_val_map: RefCell, ConstDedupResult>>>, + pub const_val_map: RefCell, Reveal>>, } impl<'tcx> ConstDedupMap<'tcx> { @@ -519,14 +502,14 @@ impl<'tcx> ConstDedupMap<'tcx> { } #[instrument(skip(self), level = "debug")] - fn insert_alloc(&self, id: GlobalId<'tcx>, val: ConstDedupResult>) { + fn insert_alloc(&self, id: GlobalId<'tcx>, val: Reveal) { let mut alloc_map = self.alloc_map.borrow_mut(); alloc_map.insert(id, val); debug!("alloc_map after update: {:#?}", alloc_map); } #[instrument(skip(self), level = "debug")] - fn insert_const_val(&self, id: GlobalId<'tcx>, val: ConstDedupResult>) { + fn insert_const_val(&self, id: GlobalId<'tcx>, val: Reveal) { let mut const_val_map = self.const_val_map.borrow_mut(); const_val_map.insert(id, val); debug!("const_val_map after update: {:#?}", const_val_map); @@ -645,7 +628,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Store the result of a call to `eval_to_allocation_raw` in order to /// allow deduplication. #[instrument(skip(self), level = "debug")] - pub fn save_alloc_for_dedup(self, id: GlobalId<'tcx>, val: ConstDedupResult>) { + pub fn save_alloc_for_dedup(self, id: GlobalId<'tcx>, val: Reveal) { let dedup_const_map = self.dedup_const_map.lock(); dedup_const_map.insert_alloc(id, val); debug!("dedup_const_map after insert: {:#?}", dedup_const_map); @@ -653,11 +636,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Store the result of a call to `eval_to_const_value_raw` in order to deduplicate it. #[instrument(skip(self), level = "debug")] - pub fn save_const_value_for_dedup( - self, - id: GlobalId<'tcx>, - val: ConstDedupResult>, - ) { + pub fn save_const_value_for_dedup(self, id: GlobalId<'tcx>, val: Reveal) { let dedup_const_map = self.dedup_const_map.lock(); dedup_const_map.insert_const_val(id, val); debug!("dedup_const_map after insert: {:#?}", dedup_const_map); @@ -671,31 +650,72 @@ impl<'tcx> TyCtxt<'tcx> { key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, opt_span: Option, ) -> EvalToAllocationRawResult<'tcx> { - use ConstDedupResult::*; - let (param_env, id) = key.into_parts(); let dedup_const_map = self.dedup_const_map.lock(); debug!("dedup_const_map: {:#?}", dedup_const_map); let alloc_map = dedup_const_map.alloc_map.borrow(); debug!("alloc_map: {:#?}", alloc_map); - let dedup_result = alloc_map.get(&id); - debug!(?dedup_result); + let dedup_reveal = alloc_map.get(&id); + debug!(?dedup_reveal); match param_env.reveal() { - Reveal::Selection => match dedup_result { - Some(Selection(alloc) | UserFacing(alloc)) => return Ok(*alloc), + Reveal::Selection => match dedup_reveal { + Some(Reveal::Selection) => { + drop(alloc_map); + drop(dedup_const_map); + + // Use cached result of query + return self.eval_to_allocation_raw(key); + } + Some(Reveal::UserFacing) => { + drop(alloc_map); + drop(dedup_const_map); + + // can deduplicate query with Reveal::Selection from Reveal::UserFacing + // since these only differ in the way errors are reported, but successful + // query calls are equivalent. + let new_key = param_env.with_user_facing().and(id); + return self.eval_to_allocation_raw(new_key); + } _ => {} }, - Reveal::UserFacing => match dedup_result { - Some(Selection(alloc) | UserFacing(alloc)) => { - return Ok(*alloc); + Reveal::UserFacing => match dedup_reveal { + Some(Reveal::UserFacing) => { + drop(alloc_map); + drop(dedup_const_map); + + return self.eval_to_allocation_raw(key); + } + Some(Reveal::Selection) => { + drop(alloc_map); + drop(dedup_const_map); + + let new_key = param_env.with_reveal_selection().and(id); + return self.eval_to_allocation_raw(new_key); } _ => {} }, - Reveal::All => match dedup_result { - Some(Selection(alloc) | UserFacing(alloc) | All(alloc)) => { - return Ok(*alloc); + Reveal::All => match dedup_reveal { + Some(Reveal::Selection) => { + drop(alloc_map); + drop(dedup_const_map); + + let new_key = param_env.with_reveal_selection().and(id); + return self.eval_to_allocation_raw(new_key); + } + Some(Reveal::UserFacing) => { + drop(alloc_map); + drop(dedup_const_map); + + let new_key = param_env.with_user_facing().and(id); + return self.eval_to_allocation_raw(new_key); + } + Some(Reveal::All) => { + drop(alloc_map); + drop(dedup_const_map); + + return self.eval_to_allocation_raw(key); } _ => {} }, @@ -722,31 +742,72 @@ impl<'tcx> TyCtxt<'tcx> { key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, opt_span: Option, ) -> EvalToConstValueResult<'tcx> { - use ConstDedupResult::*; - let (param_env, id) = key.into_parts(); let dedup_const_map = self.dedup_const_map.lock(); debug!("dedup_const_map: {:#?}", dedup_const_map); let const_val_map = dedup_const_map.const_val_map.borrow(); debug!("const_val_map: {:#?}", const_val_map); - let dedup_result = const_val_map.get(&id); - debug!(?dedup_result); + let dedup_reveal = const_val_map.get(&id); + debug!(?dedup_reveal); match param_env.reveal() { - Reveal::Selection => match dedup_result { - Some(Selection(const_val) | UserFacing(const_val)) => return Ok(*const_val), + Reveal::Selection => match dedup_reveal { + Some(Reveal::Selection) => { + drop(const_val_map); + drop(dedup_const_map); + + // Use cached result of query + return self.eval_to_const_value_raw(key); + } + Some(Reveal::UserFacing) => { + drop(const_val_map); + drop(dedup_const_map); + + // can deduplicate query with Reveal::Selection from Reveal::UserFacing + // since these only differ in the way errors are reported, but successful + // query calls are equivalent. + let new_key = param_env.with_user_facing().and(id); + return self.eval_to_const_value_raw(new_key); + } _ => {} }, - Reveal::UserFacing => match dedup_result { - Some(Selection(const_value) | UserFacing(const_value)) => { - return Ok(*const_value); + Reveal::UserFacing => match dedup_reveal { + Some(Reveal::UserFacing) => { + drop(const_val_map); + drop(dedup_const_map); + + return self.eval_to_const_value_raw(key); + } + Some(Reveal::Selection) => { + drop(const_val_map); + drop(dedup_const_map); + + let new_key = param_env.with_reveal_selection().and(id); + return self.eval_to_const_value_raw(new_key); } _ => {} }, - Reveal::All => match dedup_result { - Some(Selection(const_value) | UserFacing(const_value) | All(const_value)) => { - return Ok(*const_value); + Reveal::All => match dedup_reveal { + Some(Reveal::Selection) => { + drop(const_val_map); + drop(dedup_const_map); + + let new_key = param_env.with_reveal_selection().and(id); + return self.eval_to_const_value_raw(new_key); + } + Some(Reveal::UserFacing) => { + drop(const_val_map); + drop(dedup_const_map); + + let new_key = param_env.with_user_facing().and(id); + return self.eval_to_const_value_raw(new_key); + } + Some(Reveal::All) => { + drop(const_val_map); + drop(dedup_const_map); + + return self.eval_to_const_value_raw(key); } _ => {} }, From 66a689c6c11e2289f59737c6d7f18eb5ccffc5ed Mon Sep 17 00:00:00 2001 From: b-naber Date: Fri, 21 Jan 2022 16:27:18 +0100 Subject: [PATCH 11/11] address review --- .../src/interpret/eval_context.rs | 14 +- .../rustc_middle/src/mir/interpret/mod.rs | 212 ++++++------------ 2 files changed, 76 insertions(+), 150 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index f73d2dff92029..fb7b026c6fb73 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -266,13 +266,6 @@ impl<'mir, 'tcx, Tag: Provenance, Extra> Frame<'mir, 'tcx, Tag, Extra> { } } -impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> { - #[inline] - fn data_layout(&self) -> &TargetDataLayout { - &self.tcx.data_layout - } -} - impl<'tcx> fmt::Display for FrameInfo<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(|tcx| { @@ -299,6 +292,13 @@ impl<'tcx> fmt::Display for FrameInfo<'tcx> { } } +impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> { + #[inline] + fn data_layout(&self) -> &TargetDataLayout { + &self.tcx.data_layout + } +} + impl<'mir, 'tcx, M> layout::HasTyCtxt<'tcx> for InterpCx<'mir, 'tcx, M> where M: Machine<'mir, 'tcx>, diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index cda1d7e1ca7c7..086224b6ff806 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -108,13 +108,11 @@ use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::{HashMapExt, Lock}; use rustc_data_structures::tiny_list::TinyList; -use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::definitions::DefPathData; use rustc_middle::traits::Reveal; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_serialize::{Decodable, Encodable}; -use rustc_span::{Pos, Span}; +use rustc_span::Span; use rustc_target::abi::Endian; use crate::mir; @@ -451,40 +449,6 @@ impl<'tcx> AllocMap<'tcx> { } } -/// What we store about a frame in an interpreter backtrace. -#[derive(Debug)] -pub struct FrameInfo<'tcx> { - pub instance: ty::Instance<'tcx>, - pub span: Span, - pub lint_root: Option, -} - -impl<'tcx> fmt::Display for FrameInfo<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ty::tls::with(|tcx| { - if tcx.def_key(self.instance.def_id()).disambiguated_data.data - == DefPathData::ClosureExpr - { - write!(f, "inside closure")?; - } else { - write!(f, "inside `{}`", self.instance)?; - } - if !self.span.is_dummy() { - let sm = tcx.sess.source_map(); - let lo = sm.lookup_char_pos(self.span.lo()); - write!( - f, - " at {}:{}:{}", - sm.filename_for_diagnostics(&lo.file.name), - lo.line, - lo.col.to_usize() + 1 - )?; - } - Ok(()) - }) - } -} - /// Used to store results of calls to `eval_to_allocation_raw` and /// `eval_to_const_value_raw`. #[derive(Debug)] @@ -650,88 +614,13 @@ impl<'tcx> TyCtxt<'tcx> { key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, opt_span: Option, ) -> EvalToAllocationRawResult<'tcx> { - let (param_env, id) = key.into_parts(); - let dedup_const_map = self.dedup_const_map.lock(); - debug!("dedup_const_map: {:#?}", dedup_const_map); - let alloc_map = dedup_const_map.alloc_map.borrow(); - debug!("alloc_map: {:#?}", alloc_map); - - let dedup_reveal = alloc_map.get(&id); - debug!(?dedup_reveal); - - match param_env.reveal() { - Reveal::Selection => match dedup_reveal { - Some(Reveal::Selection) => { - drop(alloc_map); - drop(dedup_const_map); - - // Use cached result of query - return self.eval_to_allocation_raw(key); - } - Some(Reveal::UserFacing) => { - drop(alloc_map); - drop(dedup_const_map); - - // can deduplicate query with Reveal::Selection from Reveal::UserFacing - // since these only differ in the way errors are reported, but successful - // query calls are equivalent. - let new_key = param_env.with_user_facing().and(id); - return self.eval_to_allocation_raw(new_key); - } - _ => {} - }, - Reveal::UserFacing => match dedup_reveal { - Some(Reveal::UserFacing) => { - drop(alloc_map); - drop(dedup_const_map); - - return self.eval_to_allocation_raw(key); - } - Some(Reveal::Selection) => { - drop(alloc_map); - drop(dedup_const_map); - - let new_key = param_env.with_reveal_selection().and(id); - return self.eval_to_allocation_raw(new_key); - } - _ => {} - }, - Reveal::All => match dedup_reveal { - Some(Reveal::Selection) => { - drop(alloc_map); - drop(dedup_const_map); - - let new_key = param_env.with_reveal_selection().and(id); - return self.eval_to_allocation_raw(new_key); - } - Some(Reveal::UserFacing) => { - drop(alloc_map); - drop(dedup_const_map); - - let new_key = param_env.with_user_facing().and(id); - return self.eval_to_allocation_raw(new_key); - } - Some(Reveal::All) => { - drop(alloc_map); - drop(dedup_const_map); - - return self.eval_to_allocation_raw(key); - } - _ => {} - }, - } - - // Important to drop the lock here - drop(alloc_map); - drop(dedup_const_map); - - debug!("unable to deduplicate"); - - // We weren't able to deduplicate - match opt_span { - Some(span) => self.at(span).eval_to_allocation_raw(key), - None => self.eval_to_allocation_raw(key), - } + self.dedup_eval( + key, + opt_span, + EvalCtxt::Alloc, + |key| self.eval_to_allocation_raw(key), + |key, span| self.at(span).eval_to_allocation_raw(key), + ) } /// Tries to deduplicate a call to `eval_to_const_value_raw`. If deduplication isn't @@ -742,91 +631,128 @@ impl<'tcx> TyCtxt<'tcx> { key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, opt_span: Option, ) -> EvalToConstValueResult<'tcx> { + self.dedup_eval( + key, + opt_span, + EvalCtxt::ConstVal, + |key| self.eval_to_const_value_raw(key), + |key, span| self.at(span).eval_to_const_value_raw(key), + ) + } + + /// Deduplicates the result of a query call. + /// + /// Explanation for why we can deduplicate query calls in certain situations: + /// + /// If we have stored a successful query result with `Reveal::Selection` then we can + /// deduplicate that result on calls of the `dedup` version of the query with both + /// `Reveal::Selection` and `Reveal::UserFacing` (since `Selection` and `UserFacing` + /// only differ in that we keep certain errors silent with `Selection`, but successful + /// allocations are always equivalent). This is also the reason why we can deduplicate + /// from saved calls with `UserFacing`on calls with both `Selection` and `UserFacing`. + /// + /// If we try to deduplicate with `Reveal::All` we are able to deduplicate + /// from saved query results with both `Selection` and `UserFacing`, since `All` + /// will always succeed if either `UserFacing` or `Selection` have succeeded. + /// On the other hand if we have a saved result with `Reveal::All`, it's not + /// guaranteed that query calls with `Reveal::Selection` or `Reveal::UserFacing` + /// succeed, so we can't deduplicate from those. + fn dedup_eval( + self, + key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, + opt_span: Option, + eval_ctxt: EvalCtxt, + query_call: impl Fn(ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) -> T, + query_call_span: impl Fn(ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, Span) -> T, + ) -> T { let (param_env, id) = key.into_parts(); let dedup_const_map = self.dedup_const_map.lock(); - debug!("dedup_const_map: {:#?}", dedup_const_map); - let const_val_map = dedup_const_map.const_val_map.borrow(); - debug!("const_val_map: {:#?}", const_val_map); + let dedup_query_map = match eval_ctxt { + EvalCtxt::Alloc => dedup_const_map.alloc_map.borrow(), + EvalCtxt::ConstVal => dedup_const_map.const_val_map.borrow(), + }; - let dedup_reveal = const_val_map.get(&id); + let dedup_reveal = dedup_query_map.get(&id); debug!(?dedup_reveal); match param_env.reveal() { Reveal::Selection => match dedup_reveal { Some(Reveal::Selection) => { - drop(const_val_map); + drop(dedup_query_map); drop(dedup_const_map); // Use cached result of query - return self.eval_to_const_value_raw(key); + return query_call(key); } Some(Reveal::UserFacing) => { - drop(const_val_map); + drop(dedup_query_map); drop(dedup_const_map); - // can deduplicate query with Reveal::Selection from Reveal::UserFacing - // since these only differ in the way errors are reported, but successful - // query calls are equivalent. let new_key = param_env.with_user_facing().and(id); - return self.eval_to_const_value_raw(new_key); + return query_call(new_key); } _ => {} }, Reveal::UserFacing => match dedup_reveal { Some(Reveal::UserFacing) => { - drop(const_val_map); + drop(dedup_query_map); drop(dedup_const_map); - return self.eval_to_const_value_raw(key); + return query_call(key); } Some(Reveal::Selection) => { - drop(const_val_map); + drop(dedup_query_map); drop(dedup_const_map); let new_key = param_env.with_reveal_selection().and(id); - return self.eval_to_const_value_raw(new_key); + return query_call(new_key); } _ => {} }, Reveal::All => match dedup_reveal { Some(Reveal::Selection) => { - drop(const_val_map); + drop(dedup_query_map); drop(dedup_const_map); let new_key = param_env.with_reveal_selection().and(id); - return self.eval_to_const_value_raw(new_key); + return query_call(new_key); } Some(Reveal::UserFacing) => { - drop(const_val_map); + drop(dedup_query_map); drop(dedup_const_map); let new_key = param_env.with_user_facing().and(id); - return self.eval_to_const_value_raw(new_key); + return query_call(new_key); } Some(Reveal::All) => { - drop(const_val_map); + drop(dedup_query_map); drop(dedup_const_map); - return self.eval_to_const_value_raw(key); + return query_call(key); } _ => {} }, } // Important to drop the lock here - drop(const_val_map); + drop(dedup_query_map); drop(dedup_const_map); debug!("unable to deduplicate"); // We weren't able to deduplicate match opt_span { - Some(span) => self.at(span).eval_to_const_value_raw(key), - None => self.eval_to_const_value_raw(key), + Some(span) => query_call_span(key, span), + None => query_call(key), } } } +enum EvalCtxt { + Alloc, + ConstVal, +} + //////////////////////////////////////////////////////////////////////////////// // Methods to access integers in the target endianness ////////////////////////////////////////////////////////////////////////////////