diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:11:38 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:13:23 +0000 |
commit | 20431706a863f92cb37dc512fef6e48d192aaf2c (patch) | |
tree | 2867f13f5fd5437ba628c67d7f87309ccadcd286 /compiler/rustc_const_eval | |
parent | Releasing progress-linux version 1.65.0+dfsg1-2~progress7.99u1. (diff) | |
download | rustc-20431706a863f92cb37dc512fef6e48d192aaf2c.tar.xz rustc-20431706a863f92cb37dc512fef6e48d192aaf2c.zip |
Merging upstream version 1.66.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
30 files changed, 543 insertions, 442 deletions
diff --git a/compiler/rustc_const_eval/Cargo.toml b/compiler/rustc_const_eval/Cargo.toml index 32e8233a0..e09a6d1d6 100644 --- a/compiler/rustc_const_eval/Cargo.toml +++ b/compiler/rustc_const_eval/Cargo.toml @@ -4,7 +4,6 @@ version = "0.0.0" edition = "2021" [lib] -doctest = false [dependencies] tracing = "0.1" diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 09d53331b..4977a5d6b 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -2,7 +2,6 @@ use std::error::Error; use std::fmt; use rustc_errors::Diagnostic; -use rustc_hir as hir; use rustc_middle::mir::AssertKind; use rustc_middle::ty::{layout::LayoutError, query::TyCtxtAt, ConstInt}; use rustc_span::{Span, Symbol}; @@ -23,11 +22,7 @@ pub enum ConstEvalErrKind { Abort(String), } -impl MachineStopType for ConstEvalErrKind { - fn is_hard_err(&self) -> bool { - matches!(self, Self::Panic { .. }) - } -} +impl MachineStopType for ConstEvalErrKind {} // The errors become `MachineStop` with plain strings when being raised. // `ConstEvalErr` (in `librustc_middle/mir/interpret/error.rs`) knows to @@ -69,7 +64,7 @@ pub struct ConstEvalErr<'tcx> { impl<'tcx> ConstEvalErr<'tcx> { /// Turn an interpreter error into something to report to the user. /// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace. - /// Should be called only if the error is actually going to to be reported! + /// Should be called only if the error is actually going to be reported! pub fn new<'mir, M: Machine<'mir, 'tcx>>( ecx: &InterpCx<'mir, 'tcx, M>, error: InterpErrorInfo<'tcx>, @@ -87,48 +82,10 @@ impl<'tcx> ConstEvalErr<'tcx> { ConstEvalErr { error: error.into_kind(), stacktrace, span } } - pub fn struct_error( - &self, - tcx: TyCtxtAt<'tcx>, - message: &str, - decorate: impl FnOnce(&mut Diagnostic), - ) -> ErrorHandled { - self.struct_generic(tcx, message, decorate, None) - } - pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled { self.struct_error(tcx, message, |_| {}) } - pub fn report_as_lint( - &self, - tcx: TyCtxtAt<'tcx>, - message: &str, - lint_root: hir::HirId, - span: Option<Span>, - ) -> ErrorHandled { - self.struct_generic( - tcx, - message, - |lint: &mut Diagnostic| { - // Apply the span. - if let Some(span) = span { - let primary_spans = lint.span.primary_spans().to_vec(); - // point at the actual error as the primary span - lint.replace_span_with(span); - // point to the `const` statement as a secondary span - // they don't have any label - for sp in primary_spans { - if sp != span { - lint.span_label(sp, ""); - } - } - } - }, - Some(lint_root), - ) - } - /// Create a diagnostic for this const eval error. /// /// Sets the message passed in via `message` and adds span labels with detailed error @@ -137,13 +94,12 @@ impl<'tcx> ConstEvalErr<'tcx> { /// /// If `lint_root.is_some()` report it as a lint, else report it as a hard error. /// (Except that for some errors, we ignore all that -- see `must_error` below.) - #[instrument(skip(self, tcx, decorate, lint_root), level = "debug")] - fn struct_generic( + #[instrument(skip(self, tcx, decorate), level = "debug")] + pub fn struct_error( &self, tcx: TyCtxtAt<'tcx>, message: &str, decorate: impl FnOnce(&mut Diagnostic), - lint_root: Option<hir::HirId>, ) -> ErrorHandled { let finish = |err: &mut Diagnostic, span_msg: Option<String>| { trace!("reporting const eval failure at {:?}", self.span); @@ -224,27 +180,9 @@ impl<'tcx> ConstEvalErr<'tcx> { let err_msg = self.error.to_string(); - // Regular case - emit a lint. - if let Some(lint_root) = lint_root { - // Report as lint. - let hir_id = - self.stacktrace.iter().rev().find_map(|frame| frame.lint_root).unwrap_or(lint_root); - tcx.struct_span_lint_hir( - rustc_session::lint::builtin::CONST_ERR, - hir_id, - tcx.span, - |lint| { - let mut lint = lint.build(message); - finish(&mut lint, Some(err_msg)); - lint.emit(); - }, - ); - ErrorHandled::Linted - } else { - // Report as hard error. - let mut err = struct_error(tcx, message); - finish(&mut err, Some(err_msg)); - ErrorHandled::Reported(err.emit()) - } + // Report as hard error. + let mut err = struct_error(tcx, message); + finish(&mut err, Some(err_msg)); + ErrorHandled::Reported(err.emit()) } } 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 a2f14e753..1b1052fdf 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -13,7 +13,7 @@ use rustc_middle::mir::pretty::display_allocation; use rustc_middle::traits::Reveal; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, subst::Subst, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::source_map::Span; use rustc_target::abi::{self, Abi}; use std::borrow::Cow; @@ -317,45 +317,23 @@ pub fn eval_to_allocation_raw_provider<'tcx>( match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) { Err(error) => { let err = ConstEvalErr::new(&ecx, error, None); - // Some CTFE errors raise just a lint, not a hard error; see - // <https://github.com/rust-lang/rust/issues/71800>. - let is_hard_err = if let Some(def) = def.as_local() { - // (Associated) consts only emit a lint, since they might be unused. - !matches!(tcx.def_kind(def.did.to_def_id()), DefKind::Const | DefKind::AssocConst) - // check if the inner InterpError is hard - || err.error.is_hard_err() + let msg = if is_static { + Cow::from("could not evaluate static initializer") } else { - // 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") + // If the current item has generics, we'd like to enrich the message with the + // instance and its substs: to show the actual compile-time values, in addition to + // the expression, leading to the const eval error. + let instance = &key.value.instance; + if !instance.substs.is_empty() { + let instance = with_no_trimmed_paths!(instance.to_string()); + let msg = format!("evaluation of `{}` failed", instance); + Cow::from(msg) } else { - // If the current item has generics, we'd like to enrich the message with the - // instance and its substs: to show the actual compile-time values, in addition to - // the expression, leading to the const eval error. - let instance = &key.value.instance; - if !instance.substs.is_empty() { - let instance = with_no_trimmed_paths!(instance.to_string()); - let msg = format!("evaluation of `{}` failed", instance); - Cow::from(msg) - } else { - Cow::from("evaluation of constant value failed") - } - }; + Cow::from("evaluation of constant value failed") + } + }; - Err(err.report_as_error(ecx.tcx.at(err.span), &msg)) - } else { - let hir_id = tcx.hir().local_def_id_to_hir_id(def.as_local().unwrap().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), - )) - } + Err(err.report_as_error(ecx.tcx.at(err.span), &msg)) } Ok(mplace) => { // Since evaluation had no errors, validate the resulting constant. diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index e5acacd91..35d58d2f6 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -2,10 +2,10 @@ use rustc_hir::def::DefKind; use rustc_middle::mir; use rustc_middle::ty::{self, Ty, TyCtxt}; use std::borrow::Borrow; -use std::collections::hash_map::Entry; use std::hash::Hash; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::fx::IndexEntry; use std::fmt; use rustc_ast::Mutability; @@ -107,18 +107,18 @@ impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> { } } -impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> { +impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxIndexMap<K, V> { #[inline(always)] fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool where K: Borrow<Q>, { - FxHashMap::contains_key(self, k) + FxIndexMap::contains_key(self, k) } #[inline(always)] fn insert(&mut self, k: K, v: V) -> Option<V> { - FxHashMap::insert(self, k, v) + FxIndexMap::insert(self, k, v) } #[inline(always)] @@ -126,7 +126,7 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> { where K: Borrow<Q>, { - FxHashMap::remove(self, k) + FxIndexMap::remove(self, k) } #[inline(always)] @@ -148,8 +148,8 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> { #[inline(always)] fn get_mut_or<E>(&mut self, k: K, vacant: impl FnOnce() -> Result<V, E>) -> Result<&mut V, E> { match self.entry(k) { - Entry::Occupied(e) => Ok(e.into_mut()), - Entry::Vacant(e) => { + IndexEntry::Occupied(e) => Ok(e.into_mut()), + IndexEntry::Vacant(e) => { let v = vacant()?; Ok(e.insert(v)) } diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index d9c4ae4d5..1c33e7845 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -100,10 +100,10 @@ pub(crate) fn try_destructure_mir_constant<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, val: mir::ConstantKind<'tcx>, -) -> InterpResult<'tcx, mir::DestructuredMirConstant<'tcx>> { +) -> InterpResult<'tcx, mir::DestructuredConstant<'tcx>> { trace!("destructure_mir_constant: {:?}", val); let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); - let op = ecx.mir_const_to_op(&val, None)?; + let op = ecx.const_to_op(&val, None)?; // We go to `usize` as we cannot allocate anything bigger anyway. let (field_count, variant, down) = match val.ty().kind() { @@ -129,7 +129,7 @@ pub(crate) fn try_destructure_mir_constant<'tcx>( .collect::<InterpResult<'tcx, Vec<_>>>()?; let fields = tcx.arena.alloc_from_iter(fields_iter); - Ok(mir::DestructuredMirConstant { variant, fields }) + Ok(mir::DestructuredConstant { variant, fields }) } #[instrument(skip(tcx), level = "debug")] @@ -139,7 +139,7 @@ pub(crate) fn deref_mir_constant<'tcx>( val: mir::ConstantKind<'tcx>, ) -> mir::ConstantKind<'tcx> { let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); - let op = ecx.mir_const_to_op(&val, None).unwrap(); + let op = ecx.const_to_op(&val, None).unwrap(); let mplace = ecx.deref_operand(&op).unwrap(); if let Some(alloc_id) = mplace.ptr.provenance { assert_eq!( diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index a964fe846..f4da11883 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -212,7 +212,7 @@ fn create_pointee_place<'tcx>( ) -> MPlaceTy<'tcx> { let tcx = ecx.tcx.tcx; - if !ty.is_sized(ecx.tcx, ty::ParamEnv::empty()) { + if !ty.is_sized(*ecx.tcx, ty::ParamEnv::empty()) { // We need to create `Allocation`s for custom DSTs let (unsized_inner_ty, num_elems) = get_info_on_unsized_field(ty, valtree, tcx); @@ -398,7 +398,7 @@ fn valtree_into_mplace<'tcx>( let mut place_inner = match ty.kind() { ty::Str | ty::Slice(_) => ecx.mplace_index(&place, i as u64).unwrap(), - _ if !ty.is_sized(ecx.tcx, ty::ParamEnv::empty()) + _ if !ty.is_sized(*ecx.tcx, ty::ParamEnv::empty()) && i == branches.len() - 1 => { // Note: For custom DSTs we need to manually process the last unsized field. diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index c3547cb3a..4b0550767 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -1,146 +1,146 @@ use rustc_hir::ConstContext; -use rustc_macros::SessionDiagnostic; +use rustc_macros::Diagnostic; use rustc_span::Span; -#[derive(SessionDiagnostic)] -#[diag(const_eval::unstable_in_stable)] +#[derive(Diagnostic)] +#[diag(const_eval_unstable_in_stable)] pub(crate) struct UnstableInStable { pub gate: String, #[primary_span] pub span: Span, #[suggestion( - const_eval::unstable_sugg, + unstable_sugg, code = "#[rustc_const_unstable(feature = \"...\", issue = \"...\")]\n", applicability = "has-placeholders" )] #[suggestion( - const_eval::bypass_sugg, + bypass_sugg, code = "#[rustc_allow_const_fn_unstable({gate})]\n", applicability = "has-placeholders" )] pub attr_span: Span, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::thread_local_access, code = "E0625")] +#[derive(Diagnostic)] +#[diag(const_eval_thread_local_access, code = "E0625")] pub(crate) struct NonConstOpErr { #[primary_span] pub span: Span, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::static_access, code = "E0013")] +#[derive(Diagnostic)] +#[diag(const_eval_static_access, code = "E0013")] #[help] pub(crate) struct StaticAccessErr { #[primary_span] pub span: Span, pub kind: ConstContext, - #[note(const_eval::teach_note)] - #[help(const_eval::teach_help)] + #[note(teach_note)] + #[help(teach_help)] pub teach: Option<()>, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::raw_ptr_to_int)] +#[derive(Diagnostic)] +#[diag(const_eval_raw_ptr_to_int)] #[note] -#[note(const_eval::note2)] +#[note(note2)] pub(crate) struct RawPtrToIntErr { #[primary_span] pub span: Span, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::raw_ptr_comparison)] +#[derive(Diagnostic)] +#[diag(const_eval_raw_ptr_comparison)] #[note] pub(crate) struct RawPtrComparisonErr { #[primary_span] pub span: Span, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::panic_non_str)] +#[derive(Diagnostic)] +#[diag(const_eval_panic_non_str)] pub(crate) struct PanicNonStrErr { #[primary_span] pub span: Span, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::mut_deref, code = "E0658")] +#[derive(Diagnostic)] +#[diag(const_eval_mut_deref, code = "E0658")] pub(crate) struct MutDerefErr { #[primary_span] pub span: Span, pub kind: ConstContext, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::transient_mut_borrow, code = "E0658")] +#[derive(Diagnostic)] +#[diag(const_eval_transient_mut_borrow, code = "E0658")] pub(crate) struct TransientMutBorrowErr { #[primary_span] pub span: Span, pub kind: ConstContext, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::transient_mut_borrow_raw, code = "E0658")] +#[derive(Diagnostic)] +#[diag(const_eval_transient_mut_borrow_raw, code = "E0658")] pub(crate) struct TransientMutBorrowErrRaw { #[primary_span] pub span: Span, pub kind: ConstContext, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::max_num_nodes_in_const)] +#[derive(Diagnostic)] +#[diag(const_eval_max_num_nodes_in_const)] pub(crate) struct MaxNumNodesInConstErr { #[primary_span] pub span: Span, pub global_const_id: String, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::unallowed_fn_pointer_call)] +#[derive(Diagnostic)] +#[diag(const_eval_unallowed_fn_pointer_call)] pub(crate) struct UnallowedFnPointerCall { #[primary_span] pub span: Span, pub kind: ConstContext, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::unstable_const_fn)] +#[derive(Diagnostic)] +#[diag(const_eval_unstable_const_fn)] pub(crate) struct UnstableConstFn { #[primary_span] pub span: Span, pub def_path: String, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::unallowed_mutable_refs, code = "E0764")] +#[derive(Diagnostic)] +#[diag(const_eval_unallowed_mutable_refs, code = "E0764")] pub(crate) struct UnallowedMutableRefs { #[primary_span] pub span: Span, pub kind: ConstContext, - #[note(const_eval::teach_note)] + #[note(teach_note)] pub teach: Option<()>, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::unallowed_mutable_refs_raw, code = "E0764")] +#[derive(Diagnostic)] +#[diag(const_eval_unallowed_mutable_refs_raw, code = "E0764")] pub(crate) struct UnallowedMutableRefsRaw { #[primary_span] pub span: Span, pub kind: ConstContext, - #[note(const_eval::teach_note)] + #[note(teach_note)] pub teach: Option<()>, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::non_const_fmt_macro_call, code = "E0015")] +#[derive(Diagnostic)] +#[diag(const_eval_non_const_fmt_macro_call, code = "E0015")] pub(crate) struct NonConstFmtMacroCall { #[primary_span] pub span: Span, pub kind: ConstContext, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::non_const_fn_call, code = "E0015")] +#[derive(Diagnostic)] +#[diag(const_eval_non_const_fn_call, code = "E0015")] pub(crate) struct NonConstFnCall { #[primary_span] pub span: Span, @@ -148,35 +148,35 @@ pub(crate) struct NonConstFnCall { pub kind: ConstContext, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::unallowed_op_in_const_context)] +#[derive(Diagnostic)] +#[diag(const_eval_unallowed_op_in_const_context)] pub(crate) struct UnallowedOpInConstContext { #[primary_span] pub span: Span, pub msg: String, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::unallowed_heap_allocations, code = "E0010")] +#[derive(Diagnostic)] +#[diag(const_eval_unallowed_heap_allocations, code = "E0010")] pub(crate) struct UnallowedHeapAllocations { #[primary_span] #[label] pub span: Span, pub kind: ConstContext, - #[note(const_eval::teach_note)] + #[note(teach_note)] pub teach: Option<()>, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::unallowed_inline_asm, code = "E0015")] +#[derive(Diagnostic)] +#[diag(const_eval_unallowed_inline_asm, code = "E0015")] pub(crate) struct UnallowedInlineAsm { #[primary_span] pub span: Span, pub kind: ConstContext, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::interior_mutable_data_refer, code = "E0492")] +#[derive(Diagnostic)] +#[diag(const_eval_interior_mutable_data_refer, code = "E0492")] pub(crate) struct InteriorMutableDataRefer { #[primary_span] #[label] @@ -184,12 +184,12 @@ pub(crate) struct InteriorMutableDataRefer { #[help] pub opt_help: Option<()>, pub kind: ConstContext, - #[note(const_eval::teach_note)] + #[note(teach_note)] pub teach: Option<()>, } -#[derive(SessionDiagnostic)] -#[diag(const_eval::interior_mutability_borrow)] +#[derive(Diagnostic)] +#[diag(const_eval_interior_mutability_borrow)] pub(crate) struct InteriorMutabilityBorrow { #[primary_span] pub span: Span, diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index cbe985480..269ae15d4 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -43,9 +43,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.write_immediate(res, dest)?; } - Misc => { + IntToInt | IntToFloat => { let src = self.read_immediate(src)?; - let res = self.misc_cast(&src, cast_ty)?; + let res = self.int_to_int_or_float(&src, cast_ty)?; + self.write_immediate(res, dest)?; + } + + FloatToFloat | FloatToInt => { + let src = self.read_immediate(src)?; + let res = self.float_to_float_or_int(&src, cast_ty)?; + self.write_immediate(res, dest)?; + } + + FnPtrToPtr | PtrToPtr => { + let src = self.read_immediate(&src)?; + let res = self.ptr_to_ptr(&src, cast_ty)?; self.write_immediate(res, dest)?; } @@ -126,13 +138,25 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(()) } - pub fn misc_cast( - &mut self, + /// Handles 'IntToInt' and 'IntToFloat' casts. + pub fn int_to_int_or_float( + &self, + src: &ImmTy<'tcx, M::Provenance>, + cast_ty: Ty<'tcx>, + ) -> InterpResult<'tcx, Immediate<M::Provenance>> { + assert!(src.layout.ty.is_integral() || src.layout.ty.is_char() || src.layout.ty.is_bool()); + assert!(cast_ty.is_floating_point() || cast_ty.is_integral() || cast_ty.is_char()); + + Ok(self.cast_from_int_like(src.to_scalar(), src.layout, cast_ty)?.into()) + } + + /// Handles 'FloatToFloat' and 'FloatToInt' casts. + pub fn float_to_float_or_int( + &self, src: &ImmTy<'tcx, M::Provenance>, cast_ty: Ty<'tcx>, ) -> InterpResult<'tcx, Immediate<M::Provenance>> { use rustc_type_ir::sty::TyKind::*; - trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, cast_ty); match src.layout.ty.kind() { // Floating point @@ -142,47 +166,42 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Float(FloatTy::F64) => { return Ok(self.cast_from_float(src.to_scalar().to_f64()?, cast_ty).into()); } - // The rest is integer/pointer-"like", including fn ptr casts - _ => assert!( - src.layout.ty.is_bool() - || src.layout.ty.is_char() - || src.layout.ty.is_integral() - || src.layout.ty.is_any_ptr(), - "Unexpected cast from type {:?}", - src.layout.ty - ), + _ => { + bug!("Can't cast 'Float' type into {:?}", cast_ty); + } } + } - // # First handle non-scalar source values. - + /// Handles 'FnPtrToPtr' and 'PtrToPtr' casts. + pub fn ptr_to_ptr( + &self, + src: &ImmTy<'tcx, M::Provenance>, + cast_ty: Ty<'tcx>, + ) -> InterpResult<'tcx, Immediate<M::Provenance>> { + assert!(src.layout.ty.is_any_ptr()); + assert!(cast_ty.is_unsafe_ptr()); // Handle casting any ptr to raw ptr (might be a fat ptr). - if src.layout.ty.is_any_ptr() && cast_ty.is_unsafe_ptr() { - let dest_layout = self.layout_of(cast_ty)?; - if dest_layout.size == src.layout.size { - // Thin or fat pointer that just hast the ptr kind of target type changed. - return Ok(**src); - } else { - // Casting the metadata away from a fat ptr. - assert_eq!(src.layout.size, 2 * self.pointer_size()); - assert_eq!(dest_layout.size, self.pointer_size()); - assert!(src.layout.ty.is_unsafe_ptr()); - return match **src { - Immediate::ScalarPair(data, _) => Ok(data.into()), - Immediate::Scalar(..) => span_bug!( - self.cur_span(), - "{:?} input to a fat-to-thin cast ({:?} -> {:?})", - *src, - src.layout.ty, - cast_ty - ), - Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)), - }; - } + let dest_layout = self.layout_of(cast_ty)?; + if dest_layout.size == src.layout.size { + // Thin or fat pointer that just hast the ptr kind of target type changed. + return Ok(**src); + } else { + // Casting the metadata away from a fat ptr. + assert_eq!(src.layout.size, 2 * self.pointer_size()); + assert_eq!(dest_layout.size, self.pointer_size()); + assert!(src.layout.ty.is_unsafe_ptr()); + return match **src { + Immediate::ScalarPair(data, _) => Ok(data.into()), + Immediate::Scalar(..) => span_bug!( + self.cur_span(), + "{:?} input to a fat-to-thin cast ({:?} -> {:?})", + *src, + src.layout.ty, + cast_ty + ), + Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)), + }; } - - // # The remaining source values are scalar and "int-like". - let scalar = src.to_scalar(); - Ok(self.cast_from_int_like(scalar, src.layout, cast_ty)?.into()) } pub fn pointer_expose_address_cast( @@ -203,7 +222,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } pub fn pointer_from_exposed_address_cast( - &mut self, + &self, src: &ImmTy<'tcx, M::Provenance>, cast_ty: Ty<'tcx>, ) -> InterpResult<'tcx, Immediate<M::Provenance>> { @@ -220,6 +239,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(Scalar::from_maybe_pointer(ptr, self).into()) } + /// Low-level cast helper function. This works directly on scalars and can take 'int-like' input + /// type (basically everything with a scalar layout) to int/float/char types. pub fn cast_from_int_like( &self, scalar: Scalar<M::Provenance>, // input value (there is no ScalarTy so we separate data+layout) @@ -259,6 +280,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }) } + /// Low-level cast helper function. Converts an apfloat `f` into int or float types. fn cast_from_float<F>(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar<M::Provenance> where F: Float + Into<Scalar<M::Provenance>> + FloatConvert<Single> + FloatConvert<Double>, diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index d37eaeed0..a9063ad31 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -258,6 +258,9 @@ impl<'tcx> fmt::Display for FrameInfo<'tcx> { { write!(f, "inside closure")?; } else { + // Note: this triggers a `good_path_bug` state, which means that if we ever get here + // we must emit a diagnostic. We should never display a `FrameInfo` unless we + // actually want to emit a warning or error to the user. write!(f, "inside `{}`", self.instance)?; } if !self.span.is_dummy() { @@ -465,7 +468,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline] pub fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool { - ty.is_freeze(self.tcx, self.param_env) + ty.is_freeze(*self.tcx, self.param_env) } pub fn load_mir( @@ -683,11 +686,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.stack_mut().push(frame); // Make sure all the constants required by this frame evaluate successfully (post-monomorphization check). - for const_ in &body.required_consts { - let span = const_.span; - let const_ = - self.subst_from_current_frame_and_normalize_erasing_regions(const_.literal)?; - self.mir_const_to_op(&const_, None).map_err(|err| { + for ct in &body.required_consts { + let span = ct.span; + let ct = self.subst_from_current_frame_and_normalize_erasing_regions(ct.literal)?; + self.const_to_op(&ct, None).map_err(|err| { // If there was an error, set the span of the current frame to this constant. // Avoiding doing this when evaluation succeeds. self.frame_mut().loc = Err(span); @@ -929,11 +931,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } #[must_use] - pub fn generate_stacktrace(&self) -> Vec<FrameInfo<'tcx>> { + pub fn generate_stacktrace_from_stack( + stack: &[Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>], + ) -> Vec<FrameInfo<'tcx>> { let mut frames = Vec::new(); // This deliberately does *not* honor `requires_caller_location` since it is used for much // more than just panics. - for frame in self.stack().iter().rev() { + for frame in stack.iter().rev() { let lint_root = frame.current_source_info().and_then(|source_info| { match &frame.body.source_scopes[source_info.scope].local_data { mir::ClearCrossCrate::Set(data) => Some(data.lint_root), @@ -947,6 +951,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { trace!("generate stacktrace: {:#?}", frames); frames } + + #[must_use] + pub fn generate_stacktrace(&self) -> Vec<FrameInfo<'tcx>> { + Self::generate_stacktrace_from_stack(self.stack()) + } } #[doc(hidden)] diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 24dbc7695..6809a42dc 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -15,7 +15,7 @@ //! that contains allocations whose mutability we cannot identify.) use super::validity::RefTracking; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_middle::mir::interpret::InterpResult; @@ -37,7 +37,7 @@ pub trait CompileTimeMachine<'mir, 'tcx, T> = Machine< ExtraFnVal = !, FrameExtra = (), AllocExtra = (), - MemoryMap = FxHashMap<AllocId, (MemoryKind<T>, Allocation)>, + MemoryMap = FxIndexMap<AllocId, (MemoryKind<T>, Allocation)>, >; struct InternVisitor<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval::MemoryKind>> { @@ -47,7 +47,7 @@ struct InternVisitor<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_ev ref_tracking: &'rt mut RefTracking<(MPlaceTy<'tcx>, InternMode)>, /// A list of all encountered allocations. After type-based interning, we traverse this list to /// also intern allocations that are only referenced by a raw pointer or inside a union. - leftover_allocations: &'rt mut FxHashSet<AllocId>, + leftover_allocations: &'rt mut FxIndexSet<AllocId>, /// The root kind of the value that we're looking at. This field is never mutated for a /// particular allocation. It is primarily used to make as many allocations as possible /// read-only so LLVM can place them in const memory. @@ -79,7 +79,7 @@ struct IsStaticOrFn; /// to account for (e.g. for vtables). fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval::MemoryKind>>( ecx: &'rt mut InterpCx<'mir, 'tcx, M>, - leftover_allocations: &'rt mut FxHashSet<AllocId>, + leftover_allocations: &'rt mut FxIndexSet<AllocId>, alloc_id: AllocId, mode: InternMode, ty: Option<Ty<'tcx>>, @@ -114,7 +114,7 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval: if let InternMode::Static(mutability) = mode { // For this, we need to take into account `UnsafeCell`. When `ty` is `None`, we assume // no interior mutability. - let frozen = ty.map_or(true, |ty| ty.is_freeze(ecx.tcx, ecx.param_env)); + let frozen = ty.map_or(true, |ty| ty.is_freeze(*ecx.tcx, ecx.param_env)); // For statics, allocation mutability is the combination of place mutability and // type mutability. // The entire allocation needs to be mutable if it contains an `UnsafeCell` anywhere. @@ -332,8 +332,6 @@ pub enum InternKind { /// /// This *cannot raise an interpreter error*. Doing so is left to validation, which /// tracks where in the value we are and thus can show much better error messages. -/// Any errors here would anyway be turned into `const_err` lints, whereas validation failures -/// are hard errors. #[instrument(level = "debug", skip(ecx))] pub fn intern_const_alloc_recursive< 'mir, @@ -357,7 +355,7 @@ pub fn intern_const_alloc_recursive< // `leftover_allocations` collects *all* allocations we see, because some might not // be available in a typed way. They get interned at the end. let mut ref_tracking = RefTracking::empty(); - let leftover_allocations = &mut FxHashSet::default(); + let leftover_allocations = &mut FxIndexSet::default(); // start with the outermost allocation intern_shallow( diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs index 91f4f0425..0e3867557 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs @@ -4,7 +4,6 @@ use rustc_ast::Mutability; use rustc_hir::lang_items::LangItem; use rustc_middle::mir::TerminatorKind; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::subst::Subst; use rustc_span::{Span, Symbol}; use crate::interpret::{ diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/type_name.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/type_name.rs index 7e4c5fcb0..ffdb8de5b 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/type_name.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/type_name.rs @@ -4,7 +4,7 @@ use rustc_hir::definitions::DisambiguatedDefPathData; use rustc_middle::mir::interpret::{Allocation, ConstAllocation}; use rustc_middle::ty::{ self, - print::{PrettyPrinter, Print, Printer}, + print::{with_no_verbose_constants, PrettyPrinter, Print, Printer}, subst::{GenericArg, GenericArgKind}, Ty, TyCtxt, }; @@ -190,7 +190,9 @@ impl Write for AbsolutePathPrinter<'_> { /// Directly returns an `Allocation` containing an absolute path representation of the given type. pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAllocation<'tcx> { - let path = AbsolutePathPrinter { tcx, path: String::new() }.print_type(ty).unwrap().path; + let path = with_no_verbose_constants!( + AbsolutePathPrinter { tcx, path: String::new() }.print_type(ty).unwrap().path + ); let alloc = Allocation::from_bytes_byte_aligned_immutable(path.into_bytes()); tcx.intern_const_alloc(alloc) } diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 530e252b7..351152eba 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -426,7 +426,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { type ExtraFnVal = !; type MemoryMap = - rustc_data_structures::fx::FxHashMap<AllocId, (MemoryKind<Self::MemoryKind>, Allocation)>; + rustc_data_structures::fx::FxIndexMap<AllocId, (MemoryKind<Self::MemoryKind>, Allocation)>; const GLOBAL_KIND: Option<Self::MemoryKind> = None; // no copying of globals from `tcx` to machine memory type AllocExtra = (); diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index ed155fbfe..e5e015c1e 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -794,7 +794,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { todo.extend(static_roots); while let Some(id) = todo.pop() { if reachable.insert(id) { - // This is a new allocation, add the allocation it points to to `todo`. + // This is a new allocation, add the allocation it points to `todo`. if let Some((_, alloc)) = self.memory.alloc_map.get(id) { todo.extend( alloc.provenance().values().filter_map(|prov| prov.get_alloc_id()), diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 0e3959b61..0c212cf59 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -534,7 +534,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // * During ConstProp, with `TooGeneric` or since the `required_consts` were not all // checked yet. // * During CTFE, since promoteds in `const`/`static` initializer bodies can fail. - self.mir_const_to_op(&val, layout)? + self.const_to_op(&val, layout)? } }; trace!("{:?}: {:?}", mir_op, *op); @@ -549,45 +549,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ops.iter().map(|op| self.eval_operand(op, None)).collect() } - // Used when the miri-engine runs into a constant and for extracting information from constants - // in patterns via the `const_eval` module - /// The `val` and `layout` are assumed to already be in our interpreter - /// "universe" (param_env). pub fn const_to_op( &self, - c: ty::Const<'tcx>, - layout: Option<TyAndLayout<'tcx>>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - match c.kind() { - ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(..) => throw_inval!(TooGeneric), - ty::ConstKind::Error(DelaySpanBugEmitted { reported, .. }) => { - throw_inval!(AlreadyReported(reported)) - } - ty::ConstKind::Unevaluated(uv) => { - // NOTE: We evaluate to a `ValTree` here as a check to ensure - // we're working with valid constants, even though we never need it. - let instance = self.resolve(uv.def, uv.substs)?; - let cid = GlobalId { instance, promoted: None }; - let _valtree = self - .tcx - .eval_to_valtree(self.param_env.and(cid))? - .unwrap_or_else(|| bug!("unable to create ValTree for {:?}", uv)); - - Ok(self.eval_to_allocation(cid)?.into()) - } - ty::ConstKind::Bound(..) | ty::ConstKind::Infer(..) => { - span_bug!(self.cur_span(), "const_to_op: Unexpected ConstKind {:?}", c) - } - ty::ConstKind::Value(valtree) => { - let ty = c.ty(); - let const_val = self.tcx.valtree_to_const_val((ty, valtree)); - self.const_val_to_op(const_val, ty, layout) - } - } - } - - pub fn mir_const_to_op( - &self, val: &mir::ConstantKind<'tcx>, layout: Option<TyAndLayout<'tcx>>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { @@ -599,7 +562,36 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // manually normalized. let val = self.tcx.normalize_erasing_regions(self.param_env, *val); match val { - mir::ConstantKind::Ty(ct) => self.const_to_op(ct, layout), + mir::ConstantKind::Ty(ct) => { + match ct.kind() { + ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(..) => { + throw_inval!(TooGeneric) + } + ty::ConstKind::Error(DelaySpanBugEmitted { reported, .. }) => { + throw_inval!(AlreadyReported(reported)) + } + ty::ConstKind::Unevaluated(uv) => { + // NOTE: We evaluate to a `ValTree` here as a check to ensure + // we're working with valid constants, even though we never need it. + let instance = self.resolve(uv.def, uv.substs)?; + let cid = GlobalId { instance, promoted: None }; + let _valtree = self + .tcx + .eval_to_valtree(self.param_env.and(cid))? + .unwrap_or_else(|| bug!("unable to create ValTree for {uv:?}")); + + Ok(self.eval_to_allocation(cid)?.into()) + } + ty::ConstKind::Bound(..) | ty::ConstKind::Infer(..) => { + span_bug!(self.cur_span(), "unexpected ConstKind in ctfe: {ct:?}") + } + ty::ConstKind::Value(valtree) => { + let ty = ct.ty(); + let const_val = self.tcx.valtree_to_const_val((ty, valtree)); + self.const_val_to_op(const_val, ty, layout) + } + } + } mir::ConstantKind::Val(val, ty) => self.const_val_to_op(val, ty, layout), mir::ConstantKind::Unevaluated(uv, _) => { let instance = self.resolve(uv.def, uv.substs)?; @@ -799,13 +791,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64", not(bootstrap)))] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(Immediate, 48); static_assert_size!(ImmTy<'_>, 64); static_assert_size!(Operand, 56); static_assert_size!(OpTy<'_>, 80); + // tidy-alphabetical-end } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index b32889290..b0625b5f4 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -280,7 +280,7 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> { #[inline(always)] #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) - pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Prov> { + pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> { self.try_as_mplace().unwrap() } } @@ -640,11 +640,17 @@ where // avoid force_allocation. let src = match self.read_immediate_raw(src)? { Ok(src_val) => { - assert!(!src.layout.is_unsized(), "cannot copy unsized immediates"); - assert!( - !dest.layout.is_unsized(), - "the src is sized, so the dest must also be sized" - ); + // FIXME(const_prop): Const-prop can possibly evaluate an + // unsized copy operation when it thinks that the type is + // actually sized, due to a trivially false where-clause + // predicate like `where Self: Sized` with `Self = dyn Trait`. + // See #102553 for an example of such a predicate. + if src.layout.is_unsized() { + throw_inval!(SizeOfUnsizedType(src.layout.ty)); + } + if dest.layout.is_unsized() { + throw_inval!(SizeOfUnsizedType(dest.layout.ty)); + } assert_eq!(src.layout.size, dest.layout.size); // Yay, we got a value that we can write directly. return if layout_compat { @@ -886,12 +892,11 @@ where mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. - static_assert_size!(MemPlaceMeta, 24); + // tidy-alphabetical-start static_assert_size!(MemPlace, 40); + static_assert_size!(MemPlaceMeta, 24); static_assert_size!(MPlaceTy<'_>, 64); - #[cfg(not(bootstrap))] static_assert_size!(Place, 40); - #[cfg(not(bootstrap))] static_assert_size!(PlaceTy<'_>, 64); + // tidy-alphabetical-end } diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 77da8f104..6b2e2bb8a 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -350,6 +350,11 @@ where ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { use rustc_middle::mir::ProjectionElem::*; Ok(match proj_elem { + OpaqueCast(ty) => { + let mut place = base.clone(); + place.layout = self.layout_of(ty)?; + place + } Field(field, _) => self.place_field(base, field.index())?, Downcast(_, variant) => self.place_downcast(base, variant)?, Deref => self.deref_operand(&self.place_to_op(base)?)?.into(), @@ -374,6 +379,11 @@ where ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { use rustc_middle::mir::ProjectionElem::*; Ok(match proj_elem { + OpaqueCast(ty) => { + let mut op = base.clone(); + op.layout = self.layout_of(ty)?; + op + } Field(field, _) => self.operand_field(base, field.index())?, Downcast(_, variant) => self.operand_downcast(base, variant)?, Deref => self.deref_operand(base)?.into(), diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 50a82aa0e..57e40e168 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -35,7 +35,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { assert_eq!(discr.layout.ty, switch_ty); // Branch to the `otherwise` case by default, if no match is found. - assert!(!targets.iter().is_empty()); let mut target_block = targets.otherwise(); for (const_int, target) in targets.iter() { diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 14aaee6ac..8aa56c275 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -15,7 +15,6 @@ use rustc_middle::mir::interpret::InterpError; use rustc_middle::ty; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_span::symbol::{sym, Symbol}; -use rustc_span::DUMMY_SP; use rustc_target::abi::{Abi, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange}; use std::hash::Hash; @@ -56,7 +55,7 @@ macro_rules! throw_validation_failure { /// This lets you use the patterns as a kind of validation list, asserting which errors /// can possibly happen: /// -/// ``` +/// ```ignore(illustrative) /// let v = try_validation!(some_fn(), some_path, { /// Foo | Bar | Baz => { "some failure" }, /// }); @@ -65,7 +64,7 @@ macro_rules! throw_validation_failure { /// The patterns must be of type `UndefinedBehaviorInfo`. /// An additional expected parameter can also be added to the failure message: /// -/// ``` +/// ```ignore(illustrative) /// let v = try_validation!(some_fn(), some_path, { /// Foo | Bar | Baz => { "some failure" } expected { "something that wasn't a failure" }, /// }); @@ -74,7 +73,7 @@ macro_rules! throw_validation_failure { /// An additional nicety is that both parameters actually take format args, so you can just write /// the format string in directly: /// -/// ``` +/// ```ignore(illustrative) /// let v = try_validation!(some_fn(), some_path, { /// Foo | Bar | Baz => { "{:?}", some_failure } expected { "{}", expected_value }, /// }); @@ -726,7 +725,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> ) -> InterpResult<'tcx> { // Special check preventing `UnsafeCell` inside unions in the inner part of constants. if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. })) { - if !op.layout.ty.is_freeze(self.ecx.tcx.at(DUMMY_SP), self.ecx.param_env) { + if !op.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.param_env) { throw_validation_failure!(self.path, { "`UnsafeCell` in a `const`" }); } } diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 9f47d302a..443c01fdb 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -10,7 +10,6 @@ Rust MIR: a lowered representation of Rust. #![feature(decl_macro)] #![feature(exact_size_is_empty)] #![feature(let_chains)] -#![cfg_attr(bootstrap, feature(let_else))] #![feature(map_try_insert)] #![feature(min_specialization)] #![feature(slice_ptr_get)] @@ -21,9 +20,8 @@ Rust MIR: a lowered representation of Rust. #![feature(trusted_step)] #![feature(try_blocks)] #![feature(yeet_expr)] -#![feature(is_some_with)] +#![feature(is_some_and)] #![recursion_limit = "256"] -#![allow(rustc::potential_query_instability)] #[macro_use] extern crate tracing; @@ -33,7 +31,6 @@ extern crate rustc_middle; pub mod const_eval; mod errors; pub mod interpret; -mod might_permit_raw_init; pub mod transform; pub mod util; @@ -62,7 +59,6 @@ pub fn provide(providers: &mut Providers) { const_eval::deref_mir_constant(tcx, param_env, value) }; providers.permits_uninit_init = - |tcx, ty| might_permit_raw_init::might_permit_raw_init(tcx, ty, InitKind::Uninit); - providers.permits_zero_init = - |tcx, ty| might_permit_raw_init::might_permit_raw_init(tcx, ty, InitKind::Zero); + |tcx, ty| util::might_permit_raw_init(tcx, ty, InitKind::UninitMitigated0x01Fill); + providers.permits_zero_init = |tcx, ty| util::might_permit_raw_init(tcx, ty, InitKind::Zero); } diff --git a/compiler/rustc_const_eval/src/might_permit_raw_init.rs b/compiler/rustc_const_eval/src/might_permit_raw_init.rs deleted file mode 100644 index 37ffa19cc..000000000 --- a/compiler/rustc_const_eval/src/might_permit_raw_init.rs +++ /dev/null @@ -1,44 +0,0 @@ -use crate::const_eval::CompileTimeInterpreter; -use crate::interpret::{InterpCx, MemoryKind, OpTy}; -use rustc_middle::ty::layout::LayoutCx; -use rustc_middle::ty::{layout::TyAndLayout, ParamEnv, TyCtxt}; -use rustc_session::Limit; -use rustc_target::abi::InitKind; - -pub fn might_permit_raw_init<'tcx>( - tcx: TyCtxt<'tcx>, - ty: TyAndLayout<'tcx>, - kind: InitKind, -) -> bool { - let strict = tcx.sess.opts.unstable_opts.strict_init_checks; - - if strict { - let machine = CompileTimeInterpreter::new( - Limit::new(0), - /*can_access_statics:*/ false, - /*check_alignment:*/ true, - ); - - let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine); - - let allocated = cx - .allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap)) - .expect("OOM: failed to allocate for uninit check"); - - if kind == InitKind::Zero { - cx.write_bytes_ptr( - allocated.ptr, - std::iter::repeat(0_u8).take(ty.layout.size().bytes_usize()), - ) - .expect("failed to write bytes for zero valid check"); - } - - let ot: OpTy<'_, _> = allocated.into(); - - // Assume that if it failed, it's a validation failure. - cx.validate_operand(&ot).is_ok() - } else { - let layout_cx = LayoutCx { tcx, param_env: ParamEnv::reveal_all() }; - ty.might_permit_raw_init(&layout_cx, kind) - } -} diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 7e15858c8..22a61774e 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -13,8 +13,11 @@ use rustc_middle::ty::{self, adjustment::PointerCast, Instance, InstanceDef, Ty, use rustc_middle::ty::{Binder, TraitPredicate, TraitRef, TypeVisitable}; use rustc_mir_dataflow::{self, Analysis}; use rustc_span::{sym, Span, Symbol}; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt; -use rustc_trait_selection::traits::SelectionContext; +use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; +use rustc_trait_selection::traits::{ + self, ObligationCauseCode, SelectionContext, TraitEngine, TraitEngineExt, +}; use std::mem; use std::ops::Deref; @@ -550,7 +553,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { unimplemented!() } - Rvalue::Cast(CastKind::Misc, _, _) => {} + Rvalue::Cast(_, _, _) => {} Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) => {} Rvalue::ShallowInitBox(_, _) => {} @@ -656,6 +659,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { ProjectionElem::ConstantIndex { .. } | ProjectionElem::Downcast(..) + | ProjectionElem::OpaqueCast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::Field(..) | ProjectionElem::Index(_) => {} @@ -733,10 +737,49 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { let obligation = Obligation::new(ObligationCause::dummy(), param_env, poly_trait_pred); - let implsrc = tcx.infer_ctxt().enter(|infcx| { + let implsrc = { + let infcx = tcx.infer_ctxt().build(); let mut selcx = SelectionContext::new(&infcx); selcx.select(&obligation) - }); + }; + + // do a well-formedness check on the trait method being called. This is because typeck only does a + // "non-const" check. This is required for correctness here. + { + let infcx = tcx.infer_ctxt().build(); + let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx); + let predicates = tcx.predicates_of(callee).instantiate(tcx, substs); + let hir_id = tcx + .hir() + .local_def_id_to_hir_id(self.body.source.def_id().expect_local()); + let cause = || { + ObligationCause::new( + terminator.source_info.span, + hir_id, + ObligationCauseCode::ItemObligation(callee), + ) + }; + let normalized = infcx.partially_normalize_associated_types_in( + cause(), + param_env, + predicates, + ); + + for p in normalized.obligations { + fulfill_cx.register_predicate_obligation(&infcx, p); + } + for obligation in traits::predicates_for_generics( + |_, _| cause(), + self.param_env, + normalized.value, + ) { + fulfill_cx.register_predicate_obligation(&infcx, obligation); + } + let errors = fulfill_cx.select_all_or_error(&infcx); + if !errors.is_empty() { + infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + } + } match implsrc { Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => { @@ -794,16 +837,15 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // improve diagnostics by showing what failed. Our requirements are stricter this time // as we are going to error again anyways. - tcx.infer_ctxt().enter(|infcx| { - if let Err(e) = implsrc { - infcx.report_selection_error( - obligation.clone(), - &obligation, - &e, - false, - ); - } - }); + let infcx = tcx.infer_ctxt().build(); + if let Err(e) = implsrc { + infcx.err_ctxt().report_selection_error( + obligation.clone(), + &obligation, + &e, + false, + ); + } self.check_op(ops::FnCallNonConst { caller, @@ -867,8 +909,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { return; } - let is_intrinsic = tcx.is_intrinsic(callee); - if !tcx.is_const_fn_raw(callee) { if !tcx.is_const_default_method(callee) { // To get to here we must have already found a const impl for the @@ -928,7 +968,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // We do not use `const` modifiers for intrinsic "functions", as intrinsics are // `extern` functions, and these have no way to get marked `const`. So instead we // use `rustc_const_(un)stable` attributes to mean that the intrinsic is `const` - if self.ccx.is_const_stable_const_fn() || is_intrinsic { + if self.ccx.is_const_stable_const_fn() || tcx.is_intrinsic(callee) { self.check_op(ops::FnCallUnstable(callee, None)); return; } @@ -968,7 +1008,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { if needs_non_const_drop { self.check_op_spanned( - ops::LiveDrop { dropped_at: Some(terminator.source_info.span) }, + ops::LiveDrop { + dropped_at: Some(terminator.source_info.span), + dropped_ty: ty_of_dropped_place, + }, err_span, ); } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 5fb4bf638..b28d70194 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -156,10 +156,9 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { }), ); - let implsrc = tcx.infer_ctxt().enter(|infcx| { - let mut selcx = SelectionContext::new(&infcx); - selcx.select(&obligation) - }); + let infcx = tcx.infer_ctxt().build(); + let mut selcx = SelectionContext::new(&infcx); + let implsrc = selcx.select(&obligation); if let Ok(Some(ImplSource::UserDefined(data))) = implsrc { let span = tcx.def_span(data.impl_def_id); @@ -422,10 +421,11 @@ impl<'tcx> NonConstOp<'tcx> for InlineAsm { } #[derive(Debug)] -pub struct LiveDrop { +pub struct LiveDrop<'tcx> { pub dropped_at: Option<Span>, + pub dropped_ty: Ty<'tcx>, } -impl<'tcx> NonConstOp<'tcx> for LiveDrop { +impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> { fn build_error( &self, ccx: &ConstCx<'_, 'tcx>, @@ -435,9 +435,13 @@ impl<'tcx> NonConstOp<'tcx> for LiveDrop { ccx.tcx.sess, span, E0493, - "destructors cannot be evaluated at compile-time" + "destructor of `{}` cannot be evaluated at compile-time", + self.dropped_ty, + ); + err.span_label( + span, + format!("the destructor for this type cannot be evaluated in {}s", ccx.const_kind()), ); - err.span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind())); if let Some(span) = self.dropped_at { err.span_label(span, "value is dropped here"); } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs index 4e210f663..d4570c598 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs @@ -1,6 +1,6 @@ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{self, BasicBlock, Location}; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{Ty, TyCtxt}; use rustc_span::{symbol::sym, Span}; use super::check::Qualifs; @@ -58,9 +58,9 @@ impl<'mir, 'tcx> std::ops::Deref for CheckLiveDrops<'mir, 'tcx> { } } -impl CheckLiveDrops<'_, '_> { - fn check_live_drop(&self, span: Span) { - ops::LiveDrop { dropped_at: None }.build_error(self.ccx, span).emit(); +impl<'tcx> CheckLiveDrops<'_, 'tcx> { + fn check_live_drop(&self, span: Span, dropped_ty: Ty<'tcx>) { + ops::LiveDrop { dropped_at: None, dropped_ty }.build_error(self.ccx, span).emit(); } } @@ -90,7 +90,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> { } if dropped_place.is_indirect() { - self.check_live_drop(terminator.source_info.span); + self.check_live_drop(terminator.source_info.span, dropped_ty); return; } @@ -101,7 +101,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> { if self.qualifs.needs_non_const_drop(self.ccx, dropped_place.local, location) { // Use the span where the dropped local was declared for the error. let span = self.body.local_decls[dropped_place.local].source_info.span; - self.check_live_drop(span); + self.check_live_drop(span, dropped_ty); } } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 6c73ef5a8..335992342 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -5,9 +5,9 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::LangItem; use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::mir; use rustc_middle::mir::*; use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty}; -use rustc_span::DUMMY_SP; use rustc_trait_selection::traits::{ self, ImplSource, Obligation, ObligationCause, SelectionContext, }; @@ -91,7 +91,7 @@ impl Qualif for HasMutInterior { } fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { - !ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) + !ty.is_freeze(cx.tcx, cx.param_env) } fn in_adt_inherently<'tcx>( @@ -167,30 +167,28 @@ impl Qualif for NeedsNonConstDrop { }), ); - cx.tcx.infer_ctxt().enter(|infcx| { - let mut selcx = SelectionContext::new(&infcx); - let Some(impl_src) = selcx.select(&obligation).ok().flatten() else { - // If we couldn't select a const destruct candidate, then it's bad - return true; - }; - - if !matches!( - impl_src, - ImplSource::ConstDestruct(_) - | ImplSource::Param(_, ty::BoundConstness::ConstIfConst) - ) { - // If our const destruct candidate is not ConstDestruct or implied by the param env, - // then it's bad - return true; - } + let infcx = cx.tcx.infer_ctxt().build(); + let mut selcx = SelectionContext::new(&infcx); + let Some(impl_src) = selcx.select(&obligation).ok().flatten() else { + // If we couldn't select a const destruct candidate, then it's bad + return true; + }; + + if !matches!( + impl_src, + ImplSource::ConstDestruct(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst) + ) { + // If our const destruct candidate is not ConstDestruct or implied by the param env, + // then it's bad + return true; + } - if impl_src.borrow_nested_obligations().is_empty() { - return false; - } + if impl_src.borrow_nested_obligations().is_empty() { + return false; + } - // If we had any errors, then it's bad - !traits::fully_solve_obligations(&infcx, impl_src.nested_obligations()).is_empty() - }) + // If we had any errors, then it's bad + !traits::fully_solve_obligations(&infcx, impl_src.nested_obligations()).is_empty() } fn in_adt_inherently<'tcx>( @@ -308,6 +306,7 @@ where ProjectionElem::Deref | ProjectionElem::Field(_, _) + | ProjectionElem::OpaqueCast(_) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(_, _) @@ -349,17 +348,13 @@ where // FIXME(valtrees): check whether const qualifs should behave the same // way for type and mir constants. let uneval = match constant.literal { - ConstantKind::Ty(ct) if matches!(ct.kind(), ty::ConstKind::Unevaluated(_)) => { - let ty::ConstKind::Unevaluated(uv) = ct.kind() else { unreachable!() }; - - Some(uv.expand()) - } - ConstantKind::Ty(_) => None, + ConstantKind::Ty(ct) if matches!(ct.kind(), ty::ConstKind::Param(_)) => None, + ConstantKind::Ty(c) => bug!("expected ConstKind::Param here, found {:?}", c), ConstantKind::Unevaluated(uv, _) => Some(uv), ConstantKind::Val(..) => None, }; - if let Some(ty::Unevaluated { def, substs: _, promoted }) = uneval { + if let Some(mir::UnevaluatedConst { def, substs: _, promoted }) = uneval { // Use qualifs of the type for the promoted. Promoteds in MIR body should be possible // only for `NeedsNonConstDrop` with precise drop checking. This is the only const // check performed after the promotion. Verify that with an assertion. @@ -367,11 +362,8 @@ where // Don't peek inside trait associated constants. if promoted.is_none() && cx.tcx.trait_of_item(def.did).is_none() { - let qualifs = if let Some((did, param_did)) = def.as_const_arg() { - cx.tcx.at(constant.span).mir_const_qualif_const_arg((did, param_did)) - } else { - cx.tcx.at(constant.span).mir_const_qualif(def.did) - }; + assert_eq!(def.const_param_did, None, "expected associated const: {def:?}"); + let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def.did); if !Q::in_qualifs(&qualifs) { return false; diff --git a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs index 60c1e4950..805e6096b 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs @@ -8,7 +8,6 @@ use rustc_middle::mir::{self, BasicBlock, Local, Location, Statement, StatementK use rustc_mir_dataflow::fmt::DebugWithContext; use rustc_mir_dataflow::JoinSemiLattice; use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces}; -use rustc_span::DUMMY_SP; use std::fmt; use std::marker::PhantomData; @@ -120,10 +119,7 @@ where /// /// [rust-lang/unsafe-code-guidelines#134]: https://github.com/rust-lang/unsafe-code-guidelines/issues/134 fn shared_borrow_allows_mutation(&self, place: mir::Place<'tcx>) -> bool { - !place - .ty(self.ccx.body, self.ccx.tcx) - .ty - .is_freeze(self.ccx.tcx.at(DUMMY_SP), self.ccx.param_env) + !place.ty(self.ccx.body, self.ccx.tcx).ty.is_freeze(self.ccx.tcx, self.ccx.param_env) } } diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index f7a7cc88a..f3ae16da4 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -13,6 +13,7 @@ //! move analysis runs after promotion on broken MIR. use rustc_hir as hir; +use rustc_middle::mir; use rustc_middle::mir::traversal::ReversePostorderIter; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -40,10 +41,6 @@ pub struct PromoteTemps<'tcx> { } impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> { - fn phase_change(&self) -> Option<MirPhase> { - Some(MirPhase::Analysis(AnalysisPhase::Initial)) - } - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // There's not really any point in promoting errorful MIR. // @@ -361,7 +358,7 @@ impl<'tcx> Validator<'_, 'tcx> { return Err(Unpromotable); } } - ProjectionElem::Downcast(..) => { + ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) => { return Err(Unpromotable); } @@ -840,7 +837,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { promoted.span = span; promoted.local_decls[RETURN_PLACE] = LocalDecl::new(ty, span); let substs = tcx.erase_regions(InternalSubsts::identity_for_item(tcx, def.did)); - let uneval = ty::Unevaluated { def, substs, promoted: Some(promoted_id) }; + let uneval = mir::UnevaluatedConst { def, substs, promoted: Some(promoted_id) }; Operand::Constant(Box::new(Constant { span, diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 4aa98cb13..81b82a21f 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -13,7 +13,6 @@ use rustc_middle::mir::{ TerminatorKind, UnOp, START_BLOCK, }; use rustc_middle::ty::fold::BottomUpFolder; -use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable, TypeVisitable}; use rustc_mir_dataflow::impls::MaybeStorageLive; use rustc_mir_dataflow::storage::always_storage_live_locals; @@ -106,7 +105,7 @@ pub fn equal_up_to_regions<'tcx>( }, ) }; - tcx.infer_ctxt().enter(|infcx| infcx.can_eq(param_env, normalize(src), normalize(dest)).is_ok()) + tcx.infer_ctxt().build().can_eq(param_env, normalize(src), normalize(dest)).is_ok() } struct TypeChecker<'a, 'tcx> { @@ -236,9 +235,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // `Operand::Copy` is only supposed to be used with `Copy` types. if let Operand::Copy(place) = operand { let ty = place.ty(&self.body.local_decls, self.tcx).ty; - let span = self.body.source_info(location).span; - if !ty.is_copy_modulo_regions(self.tcx.at(span), self.param_env) { + if !ty.is_copy_modulo_regions(self.tcx, self.param_env) { self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty)); } } @@ -285,7 +283,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { this.fail( location, format!( - "Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is {:?}", + "Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is `{:?}`", parent, f, ty, f_ty ) ) @@ -557,25 +555,40 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { check_kinds!(a, "Cannot shallow init type {:?}", ty::RawPtr(..)); } Rvalue::Cast(kind, operand, target_type) => { + let op_ty = operand.ty(self.body, self.tcx); match kind { - CastKind::Misc => { - let op_ty = operand.ty(self.body, self.tcx); - if op_ty.is_enum() { + CastKind::DynStar => { + // FIXME(dyn-star): make sure nothing needs to be done here. + } + // FIXME: Add Checks for these + CastKind::PointerFromExposedAddress + | CastKind::PointerExposeAddress + | CastKind::Pointer(_) => {} + CastKind::IntToInt | CastKind::IntToFloat => { + let input_valid = op_ty.is_integral() || op_ty.is_char() || op_ty.is_bool(); + let target_valid = target_type.is_numeric() || target_type.is_char(); + if !input_valid || !target_valid { + self.fail( + location, + format!("Wrong cast kind {kind:?} for the type {op_ty}",), + ); + } + } + CastKind::FnPtrToPtr | CastKind::PtrToPtr => { + if !(op_ty.is_any_ptr() && target_type.is_unsafe_ptr()) { + self.fail(location, "Can't cast {op_ty} into 'Ptr'"); + } + } + CastKind::FloatToFloat | CastKind::FloatToInt => { + if !op_ty.is_floating_point() || !target_type.is_numeric() { self.fail( location, format!( - "enum -> int casts should go through `Rvalue::Discriminant`: {operand:?}:{op_ty} as {target_type}", + "Trying to cast non 'Float' as {kind:?} into {target_type:?}" ), ); } } - CastKind::DynStar => { - // FIXME(dyn-star): make sure nothing needs to be done here. - } - // Nothing to check here - CastKind::PointerFromExposedAddress - | CastKind::PointerExposeAddress - | CastKind::Pointer(_) => {} } } Rvalue::Repeat(_, _) diff --git a/compiler/rustc_const_eval/src/util/might_permit_raw_init.rs b/compiler/rustc_const_eval/src/util/might_permit_raw_init.rs new file mode 100644 index 000000000..6ca712233 --- /dev/null +++ b/compiler/rustc_const_eval/src/util/might_permit_raw_init.rs @@ -0,0 +1,151 @@ +use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; +use rustc_middle::ty::{ParamEnv, TyCtxt}; +use rustc_session::Limit; +use rustc_target::abi::{Abi, FieldsShape, InitKind, Scalar, Variants}; + +use crate::const_eval::CompileTimeInterpreter; +use crate::interpret::{InterpCx, MemoryKind, OpTy}; + +/// Determines if this type permits "raw" initialization by just transmuting some memory into an +/// instance of `T`. +/// +/// `init_kind` indicates if the memory is zero-initialized or left uninitialized. We assume +/// uninitialized memory is mitigated by filling it with 0x01, which reduces the chance of causing +/// LLVM UB. +/// +/// By default we check whether that operation would cause *LLVM UB*, i.e., whether the LLVM IR we +/// generate has UB or not. This is a mitigation strategy, which is why we are okay with accepting +/// Rust UB as long as there is no risk of miscompilations. The `strict_init_checks` can be set to +/// do a full check against Rust UB instead (in which case we will also ignore the 0x01-filling and +/// to the full uninit check). +pub fn might_permit_raw_init<'tcx>( + tcx: TyCtxt<'tcx>, + ty: TyAndLayout<'tcx>, + kind: InitKind, +) -> bool { + if tcx.sess.opts.unstable_opts.strict_init_checks { + might_permit_raw_init_strict(ty, tcx, kind) + } else { + let layout_cx = LayoutCx { tcx, param_env: ParamEnv::reveal_all() }; + might_permit_raw_init_lax(ty, &layout_cx, kind) + } +} + +/// Implements the 'strict' version of the `might_permit_raw_init` checks; see that function for +/// details. +fn might_permit_raw_init_strict<'tcx>( + ty: TyAndLayout<'tcx>, + tcx: TyCtxt<'tcx>, + kind: InitKind, +) -> bool { + let machine = CompileTimeInterpreter::new( + Limit::new(0), + /*can_access_statics:*/ false, + /*check_alignment:*/ true, + ); + + let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine); + + let allocated = cx + .allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap)) + .expect("OOM: failed to allocate for uninit check"); + + if kind == InitKind::Zero { + cx.write_bytes_ptr( + allocated.ptr, + std::iter::repeat(0_u8).take(ty.layout.size().bytes_usize()), + ) + .expect("failed to write bytes for zero valid check"); + } + + let ot: OpTy<'_, _> = allocated.into(); + + // Assume that if it failed, it's a validation failure. + // This does *not* actually check that references are dereferenceable, but since all types that + // require dereferenceability also require non-null, we don't actually get any false negatives + // due to this. + cx.validate_operand(&ot).is_ok() +} + +/// Implements the 'lax' (default) version of the `might_permit_raw_init` checks; see that function for +/// details. +fn might_permit_raw_init_lax<'tcx>( + this: TyAndLayout<'tcx>, + cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, + init_kind: InitKind, +) -> bool { + let scalar_allows_raw_init = move |s: Scalar| -> bool { + match init_kind { + InitKind::Zero => { + // The range must contain 0. + s.valid_range(cx).contains(0) + } + InitKind::UninitMitigated0x01Fill => { + // The range must include an 0x01-filled buffer. + let mut val: u128 = 0x01; + for _ in 1..s.size(cx).bytes() { + // For sizes >1, repeat the 0x01. + val = (val << 8) | 0x01; + } + s.valid_range(cx).contains(val) + } + } + }; + + // Check the ABI. + let valid = match this.abi { + Abi::Uninhabited => false, // definitely UB + Abi::Scalar(s) => scalar_allows_raw_init(s), + Abi::ScalarPair(s1, s2) => scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2), + Abi::Vector { element: s, count } => count == 0 || scalar_allows_raw_init(s), + Abi::Aggregate { .. } => true, // Fields are checked below. + }; + if !valid { + // This is definitely not okay. + return false; + } + + // Special magic check for references and boxes (i.e., special pointer types). + if let Some(pointee) = this.ty.builtin_deref(false) { + let pointee = cx.layout_of(pointee.ty).expect("need to be able to compute layouts"); + // We need to ensure that the LLVM attributes `aligned` and `dereferenceable(size)` are satisfied. + if pointee.align.abi.bytes() > 1 { + // 0x01-filling is not aligned. + return false; + } + if pointee.size.bytes() > 0 { + // A 'fake' integer pointer is not sufficiently dereferenceable. + return false; + } + } + + // If we have not found an error yet, we need to recursively descend into fields. + match &this.fields { + FieldsShape::Primitive | FieldsShape::Union { .. } => {} + FieldsShape::Array { .. } => { + // Arrays never have scalar layout in LLVM, so if the array is not actually + // accessed, there is no LLVM UB -- therefore we can skip this. + } + FieldsShape::Arbitrary { offsets, .. } => { + for idx in 0..offsets.len() { + if !might_permit_raw_init_lax(this.field(cx, idx), cx, init_kind) { + // We found a field that is unhappy with this kind of initialization. + return false; + } + } + } + } + + match &this.variants { + Variants::Single { .. } => { + // All fields of this single variant have already been checked above, there is nothing + // else to do. + } + Variants::Multiple { .. } => { + // We cannot tell LLVM anything about the details of this multi-variant layout, so + // invalid values "hidden" inside the variant cannot cause LLVM trouble. + } + } + + true +} diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs index a1876bed8..7a05cfd23 100644 --- a/compiler/rustc_const_eval/src/util/mod.rs +++ b/compiler/rustc_const_eval/src/util/mod.rs @@ -3,8 +3,10 @@ mod alignment; mod call_kind; pub mod collect_writes; mod find_self_call; +mod might_permit_raw_init; pub use self::aggregate::expand_aggregate; pub use self::alignment::is_disaligned; pub use self::call_kind::{call_kind, CallDesugaringKind, CallKind}; pub use self::find_self_call::find_self_call; +pub use self::might_permit_raw_init::might_permit_raw_init; |