Auto merge of #111677 - fee1-dead-contrib:rustc_const_eval-translatable, r=oli-obk,RalfJung

Use translatable diagnostics in `rustc_const_eval`

This PR:

* adds a `no_span` parameter to `note` / `help` attributes when using `Subdiagnostic` to allow adding notes/helps without using a span
* has minor tweaks and changes to error messages
This commit is contained in:
bors 2023-06-02 05:11:49 +00:00
commit 33c3d10128
93 changed files with 2385 additions and 1128 deletions

View file

@ -209,7 +209,7 @@ pub enum TargetDataLayoutErrors<'a> {
InvalidAddressSpace { addr_space: &'a str, cause: &'a str, err: ParseIntError }, InvalidAddressSpace { addr_space: &'a str, cause: &'a str, err: ParseIntError },
InvalidBits { kind: &'a str, bit: &'a str, cause: &'a str, err: ParseIntError }, InvalidBits { kind: &'a str, bit: &'a str, cause: &'a str, err: ParseIntError },
MissingAlignment { cause: &'a str }, MissingAlignment { cause: &'a str },
InvalidAlignment { cause: &'a str, err: String }, InvalidAlignment { cause: &'a str, err: AlignFromBytesError },
InconsistentTargetArchitecture { dl: &'a str, target: &'a str }, InconsistentTargetArchitecture { dl: &'a str, target: &'a str },
InconsistentTargetPointerWidth { pointer_size: u64, target: u32 }, InconsistentTargetPointerWidth { pointer_size: u64, target: u32 },
InvalidBitsSize { err: String }, InvalidBitsSize { err: String },
@ -640,30 +640,65 @@ impl fmt::Debug for Align {
} }
} }
#[derive(Clone, Copy)]
pub enum AlignFromBytesError {
NotPowerOfTwo(u64),
TooLarge(u64),
}
impl AlignFromBytesError {
pub fn diag_ident(self) -> &'static str {
match self {
Self::NotPowerOfTwo(_) => "not_power_of_two",
Self::TooLarge(_) => "too_large",
}
}
pub fn align(self) -> u64 {
let (Self::NotPowerOfTwo(align) | Self::TooLarge(align)) = self;
align
}
}
impl fmt::Debug for AlignFromBytesError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for AlignFromBytesError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AlignFromBytesError::NotPowerOfTwo(align) => write!(f, "`{align}` is not a power of 2"),
AlignFromBytesError::TooLarge(align) => write!(f, "`{align}` is too large"),
}
}
}
impl Align { impl Align {
pub const ONE: Align = Align { pow2: 0 }; pub const ONE: Align = Align { pow2: 0 };
pub const MAX: Align = Align { pow2: 29 }; pub const MAX: Align = Align { pow2: 29 };
#[inline] #[inline]
pub fn from_bits(bits: u64) -> Result<Align, String> { pub fn from_bits(bits: u64) -> Result<Align, AlignFromBytesError> {
Align::from_bytes(Size::from_bits(bits).bytes()) Align::from_bytes(Size::from_bits(bits).bytes())
} }
#[inline] #[inline]
pub fn from_bytes(align: u64) -> Result<Align, String> { pub fn from_bytes(align: u64) -> Result<Align, AlignFromBytesError> {
// Treat an alignment of 0 bytes like 1-byte alignment. // Treat an alignment of 0 bytes like 1-byte alignment.
if align == 0 { if align == 0 {
return Ok(Align::ONE); return Ok(Align::ONE);
} }
#[cold] #[cold]
fn not_power_of_2(align: u64) -> String { fn not_power_of_2(align: u64) -> AlignFromBytesError {
format!("`{}` is not a power of 2", align) AlignFromBytesError::NotPowerOfTwo(align)
} }
#[cold] #[cold]
fn too_large(align: u64) -> String { fn too_large(align: u64) -> AlignFromBytesError {
format!("`{}` is too large", align) AlignFromBytesError::TooLarge(align)
} }
let tz = align.trailing_zeros(); let tz = align.trailing_zeros();

View file

@ -6,6 +6,7 @@ use rustc_index::IndexVec;
use rustc_middle::ty::layout::{ use rustc_middle::ty::layout::{
FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers,
}; };
use rustc_span::source_map::Spanned;
use rustc_span::SourceFile; use rustc_span::SourceFile;
use rustc_target::abi::call::FnAbi; use rustc_target::abi::call::FnAbi;
use rustc_target::abi::{Integer, Primitive}; use rustc_target::abi::{Integer, Primitive};
@ -495,25 +496,16 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for RevealAllLayoutCx<'tcx> {
fn_abi_request: FnAbiRequest<'tcx>, fn_abi_request: FnAbiRequest<'tcx>,
) -> ! { ) -> ! {
if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err { if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err {
self.0.sess.span_fatal(span, err.to_string()) self.0.sess.emit_fatal(Spanned { span, node: err })
} else { } else {
match fn_abi_request { match fn_abi_request {
FnAbiRequest::OfFnPtr { sig, extra_args } => { FnAbiRequest::OfFnPtr { sig, extra_args } => {
span_bug!( span_bug!(span, "`fn_abi_of_fn_ptr({sig}, {extra_args:?})` failed: {err:?}");
span,
"`fn_abi_of_fn_ptr({}, {:?})` failed: {}",
sig,
extra_args,
err
);
} }
FnAbiRequest::OfInstance { instance, extra_args } => { FnAbiRequest::OfInstance { instance, extra_args } => {
span_bug!( span_bug!(
span, span,
"`fn_abi_of_instance({}, {:?})` failed: {}", "`fn_abi_of_instance({instance}, {extra_args:?})` failed: {err:?}"
instance,
extra_args,
err
); );
} }
} }

View file

@ -24,7 +24,7 @@ fn set_global_alignment<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, gv: LValue<'gcc>
match Align::from_bits(min) { match Align::from_bits(min) {
Ok(min) => align = align.max(min), Ok(min) => align = align.max(min),
Err(err) => { Err(err) => {
cx.sess().emit_err(InvalidMinimumAlignment { err }); cx.sess().emit_err(InvalidMinimumAlignment { err: err.to_string() });
} }
} }
} }

View file

@ -477,7 +477,7 @@ impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
#[inline] #[inline]
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
if let LayoutError::SizeOverflow(_) = err { if let LayoutError::SizeOverflow(_) = err {
self.sess().emit_fatal(respan(span, err)) self.sess().emit_fatal(respan(span, err.into_diagnostic()))
} else { } else {
span_bug!(span, "failed to get layout for `{}`: {}", ty, err) span_bug!(span, "failed to get layout for `{}`: {}", ty, err)
} }
@ -499,21 +499,12 @@ impl<'gcc, 'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
} else { } else {
match fn_abi_request { match fn_abi_request {
FnAbiRequest::OfFnPtr { sig, extra_args } => { FnAbiRequest::OfFnPtr { sig, extra_args } => {
span_bug!( span_bug!(span, "`fn_abi_of_fn_ptr({sig}, {extra_args:?})` failed: {err:?}");
span,
"`fn_abi_of_fn_ptr({}, {:?})` failed: {}",
sig,
extra_args,
err
);
} }
FnAbiRequest::OfInstance { instance, extra_args } => { FnAbiRequest::OfInstance { instance, extra_args } => {
span_bug!( span_bug!(
span, span,
"`fn_abi_of_instance({}, {:?})` failed: {}", "`fn_abi_of_instance({instance}, {extra_args:?})` failed: {err:?}"
instance,
extra_args,
err
); );
} }
} }

View file

@ -20,8 +20,12 @@ codegen_llvm_error_writing_def_file =
codegen_llvm_from_llvm_diag = {$message} codegen_llvm_from_llvm_diag = {$message}
codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message} codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message}
codegen_llvm_invalid_minimum_alignment =
invalid minimum global alignment: {$err} codegen_llvm_invalid_minimum_alignment_not_power_of_two =
invalid minimum global alignment: {$align} is not power of 2
codegen_llvm_invalid_minimum_alignment_too_large =
invalid minimum global alignment: {$align} is too large
codegen_llvm_load_bitcode = failed to load bitcode of module "{$name}" codegen_llvm_load_bitcode = failed to load bitcode of module "{$name}"
codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$name}": {$llvm_err} codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$name}": {$llvm_err}

View file

@ -1,7 +1,9 @@
use crate::base; use crate::base;
use crate::common::{self, CodegenCx}; use crate::common::{self, CodegenCx};
use crate::debuginfo; use crate::debuginfo;
use crate::errors::{InvalidMinimumAlignment, SymbolAlreadyDefined}; use crate::errors::{
InvalidMinimumAlignmentNotPowerOfTwo, InvalidMinimumAlignmentTooLarge, SymbolAlreadyDefined,
};
use crate::llvm::{self, True}; use crate::llvm::{self, True};
use crate::type_::Type; use crate::type_::Type;
use crate::type_of::LayoutLlvmExt; use crate::type_of::LayoutLlvmExt;
@ -19,7 +21,9 @@ use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::ty::{self, Instance, Ty};
use rustc_middle::{bug, span_bug}; use rustc_middle::{bug, span_bug};
use rustc_session::config::Lto; use rustc_session::config::Lto;
use rustc_target::abi::{Align, HasDataLayout, Primitive, Scalar, Size, WrappingRange}; use rustc_target::abi::{
Align, AlignFromBytesError, HasDataLayout, Primitive, Scalar, Size, WrappingRange,
};
use std::ops::Range; use std::ops::Range;
pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<'_>) -> &'ll Value { pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<'_>) -> &'ll Value {
@ -129,9 +133,14 @@ fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align:
if let Some(min) = cx.sess().target.min_global_align { if let Some(min) = cx.sess().target.min_global_align {
match Align::from_bits(min) { match Align::from_bits(min) {
Ok(min) => align = align.max(min), Ok(min) => align = align.max(min),
Err(err) => { Err(err) => match err {
cx.sess().emit_err(InvalidMinimumAlignment { err }); AlignFromBytesError::NotPowerOfTwo(align) => {
} cx.sess().emit_err(InvalidMinimumAlignmentNotPowerOfTwo { align });
}
AlignFromBytesError::TooLarge(align) => {
cx.sess().emit_err(InvalidMinimumAlignmentTooLarge { align });
}
},
} }
} }
unsafe { unsafe {

View file

@ -969,9 +969,9 @@ impl<'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'_, 'tcx> {
#[inline] #[inline]
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
if let LayoutError::SizeOverflow(_) = err { if let LayoutError::SizeOverflow(_) = err {
self.sess().emit_fatal(Spanned { span, node: err }) self.sess().emit_fatal(Spanned { span, node: err.into_diagnostic() })
} else { } else {
span_bug!(span, "failed to get layout for `{}`: {}", ty, err) span_bug!(span, "failed to get layout for `{ty}`: {err:?}")
} }
} }
} }
@ -991,21 +991,12 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'_, 'tcx> {
} else { } else {
match fn_abi_request { match fn_abi_request {
FnAbiRequest::OfFnPtr { sig, extra_args } => { FnAbiRequest::OfFnPtr { sig, extra_args } => {
span_bug!( span_bug!(span, "`fn_abi_of_fn_ptr({sig}, {extra_args:?})` failed: {err:?}",);
span,
"`fn_abi_of_fn_ptr({}, {:?})` failed: {}",
sig,
extra_args,
err
);
} }
FnAbiRequest::OfInstance { instance, extra_args } => { FnAbiRequest::OfInstance { instance, extra_args } => {
span_bug!( span_bug!(
span, span,
"`fn_abi_of_instance({}, {:?})` failed: {}", "`fn_abi_of_instance({instance}, {extra_args:?})` failed: {err:?}",
instance,
extra_args,
err
); );
} }
} }

View file

@ -50,9 +50,15 @@ pub(crate) struct SymbolAlreadyDefined<'a> {
} }
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(codegen_llvm_invalid_minimum_alignment)] #[diag(codegen_llvm_invalid_minimum_alignment_not_power_of_two)]
pub(crate) struct InvalidMinimumAlignment { pub(crate) struct InvalidMinimumAlignmentNotPowerOfTwo {
pub err: String, pub align: u64,
}
#[derive(Diagnostic)]
#[diag(codegen_llvm_invalid_minimum_alignment_too_large)]
pub(crate) struct InvalidMinimumAlignmentTooLarge {
pub align: u64,
} }
#[derive(Diagnostic)] #[derive(Diagnostic)]

View file

@ -93,8 +93,7 @@ fn push_debuginfo_type_name<'tcx>(
Err(e) => { Err(e) => {
// Computing the layout can still fail here, e.g. if the target architecture // Computing the layout can still fail here, e.g. if the target architecture
// cannot represent the type. See https://github.com/rust-lang/rust/issues/94961. // cannot represent the type. See https://github.com/rust-lang/rust/issues/94961.
// FIXME: migrate once `rustc_middle::mir::interpret::InterpError` is translatable. tcx.sess.emit_fatal(e.into_diagnostic());
tcx.sess.fatal(format!("{}", e));
} }
} }
} else { } else {

View file

@ -1,8 +1,142 @@
const_eval_address_space_full =
there are no more free addresses in the address space
const_eval_align_check_failed = accessing memory with alignment {$has}, but alignment {$required} is required
const_eval_align_offset_invalid_align =
`align_offset` called with non-power-of-two align: {$target_align}
const_eval_alignment_check_failed =
accessing memory with alignment {$has}, but alignment {$required} is required
const_eval_already_reported =
an error has already been reported elsewhere (this should not usually be printed)
const_eval_assume_false =
`assume` called with `false`
const_eval_await_non_const =
cannot convert `{$ty}` into a future in {const_eval_const_context}s
const_eval_bounds_check_failed =
indexing out of bounds: the len is {$len} but the index is {$index}
const_eval_box_to_mut = {$front_matter}: encountered a box pointing to mutable memory in a constant
const_eval_box_to_static = {$front_matter}: encountered a box pointing to a static variable in a constant
const_eval_box_to_uninhabited = {$front_matter}: encountered a box pointing to uninhabited type {$ty}
const_eval_call_nonzero_intrinsic =
`{$name}` called on 0
const_eval_closure_call =
closures need an RFC before allowed to be called in {const_eval_const_context}s
const_eval_closure_fndef_not_const =
function defined here, but it is not `const`
const_eval_closure_non_const =
cannot call non-const closure in {const_eval_const_context}s
const_eval_consider_dereferencing =
consider dereferencing here
const_eval_const_accesses_static = constant accesses static
const_eval_const_context = {$kind ->
[const] constant
[static] static
[const_fn] constant function
*[other] {""}
}
const_eval_copy_nonoverlapping_overlapping =
`copy_nonoverlapping` called on overlapping ranges
const_eval_dangling_box_no_provenance = {$front_matter}: encountered a dangling box ({$pointer} has no provenance)
const_eval_dangling_box_out_of_bounds = {$front_matter}: encountered a dangling box (going beyond the bounds of its allocation)
const_eval_dangling_box_use_after_free = {$front_matter}: encountered a dangling box (use-after-free)
const_eval_dangling_int_pointer =
{$bad_pointer_message}: {$pointer} is a dangling pointer (it has no provenance)
const_eval_dangling_null_pointer =
{$bad_pointer_message}: null pointer is a dangling pointer (it has no provenance)
const_eval_dangling_ptr_in_final = encountered dangling pointer in final constant
const_eval_dangling_ref_no_provenance = {$front_matter}: encountered a dangling reference ({$pointer} has no provenance)
const_eval_dangling_ref_out_of_bounds = {$front_matter}: encountered a dangling reference (going beyond the bounds of its allocation)
const_eval_dangling_ref_use_after_free = {$front_matter}: encountered a dangling reference (use-after-free)
const_eval_dead_local =
accessing a dead local variable
const_eval_dealloc_immutable =
deallocating immutable allocation {$alloc}
const_eval_dealloc_incorrect_layout =
incorrect layout on deallocation: {$alloc} has size {$size} and alignment {$align}, but gave size {$size_found} and alignment {$align_found}
const_eval_dealloc_kind_mismatch =
deallocating {$alloc}, which is {$alloc_kind} memory, using {$kind} deallocation operation
const_eval_deref_coercion_non_const =
cannot perform deref coercion on `{$ty}` in {const_eval_const_context}s
.note = attempting to deref into `{$target_ty}`
.target_note = deref defined here
const_eval_deref_function_pointer =
accessing {$allocation} which contains a function
const_eval_deref_test = dereferencing pointer failed
const_eval_deref_vtable_pointer =
accessing {$allocation} which contains a vtable
const_eval_different_allocations =
`{$name}` called on pointers into different allocations
const_eval_division_by_zero =
dividing by zero
const_eval_division_overflow =
overflow in signed division (dividing MIN by -1)
const_eval_double_storage_live =
StorageLive on a local that was already live
const_eval_dyn_call_not_a_method =
`dyn` call trying to call something that is not a method
const_eval_dyn_call_vtable_mismatch =
`dyn` call on a pointer whose vtable does not match its type
const_eval_dyn_star_call_vtable_mismatch =
`dyn*` call on a pointer whose vtable does not match its type
const_eval_erroneous_constant =
erroneous constant used
const_eval_error = {$error_kind ->
[static] could not evaluate static initializer
[const] evaluation of constant value failed
[const_with_path] evaluation of `{$instance}` failed
*[other] {""}
}
const_eval_exact_div_has_remainder =
exact_div: {$a} cannot be divided by {$b} without remainder
const_eval_expected_non_ptr = {$front_matter}: encountered `{$value}`, but expected plain (non-pointer) bytes
const_eval_fn_ptr_call =
function pointers need an RFC before allowed to be called in {const_eval_const_context}s
const_eval_for_loop_into_iter_non_const =
cannot convert `{$ty}` into an iterator in {const_eval_const_context}s
const_eval_frame_note = {$times ->
[0] {const_eval_frame_note_inner}
*[other] [... {$times} additional calls {const_eval_frame_note_inner} ...]
}
const_eval_frame_note_inner = inside {$where_ ->
[closure] closure
[instance] `{$instance}`
*[other] {""}
}
const_eval_in_bounds_test = out-of-bounds pointer use
const_eval_incompatible_calling_conventions =
calling a function with calling convention {$callee_conv} using calling convention {$caller_conv}
const_eval_incompatible_return_types =
calling a function with return type {$callee_ty} passing return place of type {$caller_ty}
const_eval_incompatible_types =
calling a function with argument of type {$callee_ty} passing data of type {$caller_ty}
const_eval_interior_mutability_borrow = const_eval_interior_mutability_borrow =
cannot borrow here, since the borrowed element may contain interior mutability cannot borrow here, since the borrowed element may contain interior mutability
const_eval_interior_mutable_data_refer = const_eval_interior_mutable_data_refer =
{$kind}s cannot refer to interior mutable data {const_eval_const_context}s cannot refer to interior mutable data
.label = this borrow of an interior mutable value may end up in the final value .label = this borrow of an interior mutable value may end up in the final value
.help = to fix this, the value can be extracted to a separate `static` item and then referenced .help = to fix this, the value can be extracted to a separate `static` item and then referenced
.teach_note = .teach_note =
@ -10,25 +144,163 @@ const_eval_interior_mutable_data_refer =
This would make multiple uses of a constant to be able to see different values and allow circumventing This would make multiple uses of a constant to be able to see different values and allow circumventing
the `Send` and `Sync` requirements for shared mutable data, which is unsound. the `Send` and `Sync` requirements for shared mutable data, which is unsound.
const_eval_invalid_align =
align has to be a power of 2
const_eval_invalid_align_details =
invalid align passed to `{$name}`: {$align} is {$err_kind ->
[not_power_of_two] not a power of 2
[too_large] too large
*[other] {""}
}
const_eval_invalid_bool =
interpreting an invalid 8-bit value as a bool: 0x{$value}
const_eval_invalid_box_meta = {$front_matter}: encountered invalid box metadata: total size is bigger than largest supported object
const_eval_invalid_box_slice_meta = {$front_matter}: encountered invalid box metadata: slice is bigger than largest supported object
const_eval_invalid_char =
interpreting an invalid 32-bit value as a char: 0x{$value}
const_eval_invalid_dealloc =
deallocating {$alloc_id}, which is {$kind ->
[fn] a function
[vtable] a vtable
[static_mem] static memory
*[other] {""}
}
const_eval_invalid_enum_tag = {$front_matter}: encountered {$value}, but expected a valid enum tag
const_eval_invalid_fn_ptr = {$front_matter}: encountered {$value}, but expected a function pointer
const_eval_invalid_function_pointer =
using {$pointer} as function pointer but it does not point to a function
const_eval_invalid_meta =
invalid metadata in wide pointer: total size is bigger than largest supported object
const_eval_invalid_meta_slice =
invalid metadata in wide pointer: slice is bigger than largest supported object
const_eval_invalid_ref_meta = {$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object
const_eval_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object
const_eval_invalid_str =
this string is not valid UTF-8: {$err}
const_eval_invalid_tag =
enum value has invalid tag: {$tag}
const_eval_invalid_transmute =
transmuting from {$src_bytes}-byte type to {$dest_bytes}-byte type: `{$src}` -> `{$dest}`
const_eval_invalid_uninit_bytes =
reading memory at {$alloc}{$access}, but memory is uninitialized at {$uninit}, and this operation requires initialized memory
const_eval_invalid_uninit_bytes_unknown =
using uninitialized data, but this operation requires initialized memory
const_eval_invalid_value = constructing invalid value
const_eval_invalid_value_with_path = constructing invalid value at {$path}
## The `front_matter`s here refer to either `middle_invalid_value` or `middle_invalid_value_with_path`.
const_eval_invalid_vtable_pointer =
using {$pointer} as vtable pointer but it does not point to a vtable
const_eval_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer
const_eval_live_drop =
destructor of `{$dropped_ty}` cannot be evaluated at compile-time
.label = the destructor for this type cannot be evaluated in {const_eval_const_context}s
.dropped_at_label = value is dropped here
const_eval_long_running = const_eval_long_running =
constant evaluation is taking a long time constant evaluation is taking a long time
.note = this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval. .note = this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval.
If your compilation actually takes a long time, you can safely allow the lint. If your compilation actually takes a long time, you can safely allow the lint.
.label = the const evaluator is currently interpreting this expression .label = the const evaluator is currently interpreting this expression
.help = the constant being evaluated .help = the constant being evaluated
const_eval_max_num_nodes_in_const = maximum number of nodes exceeded in constant {$global_const_id} const_eval_max_num_nodes_in_const = maximum number of nodes exceeded in constant {$global_const_id}
const_eval_mut_deref = const_eval_memory_access_test = memory access failed
mutation through a reference is not allowed in {$kind}s const_eval_memory_exhausted =
tried to allocate more memory than available to compiler
const_eval_modified_global =
modifying a static's initial value from another static's initializer
const_eval_mut_deref =
mutation through a reference is not allowed in {const_eval_const_context}s
const_eval_mutable_ref_in_const = {$front_matter}: encountered mutable reference in a `const`
const_eval_never_val = {$front_matter}: encountered a value of the never type `!`
const_eval_non_const_fmt_macro_call = const_eval_non_const_fmt_macro_call =
cannot call non-const formatting macro in {$kind}s cannot call non-const formatting macro in {const_eval_const_context}s
const_eval_non_const_fn_call = const_eval_non_const_fn_call =
cannot call non-const fn `{$def_path_str}` in {$kind}s cannot call non-const fn `{$def_path_str}` in {const_eval_const_context}s
const_eval_non_const_impl =
impl defined here, but it is not `const`
const_eval_noreturn_asm_returned =
returned from noreturn inline assembly
const_eval_not_enough_caller_args =
calling a function with fewer arguments than it requires
const_eval_null_box = {$front_matter}: encountered a null box
const_eval_null_fn_ptr = {$front_matter}: encountered a null function pointer
const_eval_null_ref = {$front_matter}: encountered a null reference
const_eval_nullable_ptr_out_of_range = {$front_matter}: encountered a potentially null pointer, but expected something that cannot possibly fail to be {$in_range}
const_eval_nullary_intrinsic_fail =
could not evaluate nullary intrinsic
const_eval_offset_from_overflow =
`{$name}` called when first pointer is too far ahead of second
const_eval_offset_from_test = out-of-bounds `offset_from`
const_eval_offset_from_underflow =
`{$name}` called when first pointer is too far before second
const_eval_operator_non_const =
cannot call non-const operator in {const_eval_const_context}s
const_eval_out_of_range = {$front_matter}: encountered {$value}, but expected something {$in_range}
const_eval_overflow =
overflow executing `{$name}`
const_eval_overflow_shift =
overflowing shift by {$val} in `{$name}`
const_eval_panic =
the evaluated program panicked at '{$msg}', {$file}:{$line}:{$col}
const_eval_panic_non_str = argument to `panic!()` in a const context must have type `&str` const_eval_panic_non_str = argument to `panic!()` in a const context must have type `&str`
const_eval_partial_pointer_copy =
unable to copy parts of a pointer from memory at {$ptr}
const_eval_partial_pointer_overwrite =
unable to overwrite parts of a pointer in memory at {$ptr}
const_eval_pointer_arithmetic_overflow =
overflowing in-bounds pointer arithmetic
const_eval_pointer_arithmetic_test = out-of-bounds pointer arithmetic
const_eval_pointer_out_of_bounds =
{$bad_pointer_message}: {$alloc_id} has size {$alloc_size}, so pointer to {$ptr_size} {$ptr_size ->
[1] byte
*[many] bytes
} starting at offset {$ptr_offset} is out-of-bounds
const_eval_pointer_use_after_free =
pointer to {$allocation} was dereferenced after this allocation got freed
const_eval_ptr_as_bytes_1 =
this code performed an operation that depends on the underlying bytes representing a pointer
const_eval_ptr_as_bytes_2 =
the absolute address of a pointer is not known at compile-time, so such operations are not supported
const_eval_ptr_out_of_range = {$front_matter}: encountered a pointer, but expected something that cannot possibly fail to be {$in_range}
const_eval_question_branch_non_const =
`?` cannot determine the branch of `{$ty}` in {const_eval_const_context}s
const_eval_question_from_residual_non_const =
`?` cannot convert from residual of `{$ty}` in {const_eval_const_context}s
const_eval_range = in the range {$lo}..={$hi}
const_eval_range_lower = greater or equal to {$lo}
const_eval_range_singular = equal to {$lo}
const_eval_range_upper = less or equal to {$hi}
const_eval_range_wrapping = less or equal to {$hi}, or greater or equal to {$lo}
const_eval_raw_bytes = the raw bytes of the constant (size: {$size}, align: {$align}) {"{"}{$bytes}{"}"}
const_eval_raw_eq_with_provenance =
`raw_eq` on bytes with provenance
const_eval_raw_ptr_comparison = const_eval_raw_ptr_comparison =
pointers cannot be reliably compared during const eval pointers cannot be reliably compared during const eval
.note = see issue #53020 <https://github.com/rust-lang/rust/issues/53020> for more information .note = see issue #53020 <https://github.com/rust-lang/rust/issues/53020> for more information
@ -38,8 +310,36 @@ const_eval_raw_ptr_to_int =
.note = at compile-time, pointers do not have an integer value .note = at compile-time, pointers do not have an integer value
.note2 = avoiding this restriction via `transmute`, `union`, or raw pointers leads to compile-time undefined behavior .note2 = avoiding this restriction via `transmute`, `union`, or raw pointers leads to compile-time undefined behavior
const_eval_read_extern_static =
cannot read from extern static ({$did})
const_eval_read_pointer_as_bytes =
unable to turn pointer into raw bytes
const_eval_realloc_or_alloc_with_offset =
{$kind ->
[dealloc] deallocating
[realloc] reallocating
*[other] {""}
} {$ptr} which does not point to the beginning of an object
const_eval_ref_to_mut = {$front_matter}: encountered a reference pointing to mutable memory in a constant
const_eval_ref_to_static = {$front_matter}: encountered a reference pointing to a static variable in a constant
const_eval_ref_to_uninhabited = {$front_matter}: encountered a reference pointing to uninhabited type {$ty}
const_eval_remainder_by_zero =
calculating the remainder with a divisor of zero
const_eval_remainder_overflow =
overflow in signed remainder (dividing MIN by -1)
const_eval_scalar_size_mismatch =
scalar size mismatch: expected {$target_size} bytes but got {$data_size} bytes instead
const_eval_size_of_unsized =
size_of called on unsized type `{$ty}`
const_eval_size_overflow =
overflow computing total size of `{$name}`
const_eval_stack_frame_limit_reached =
reached the configured maximum number of stack frames
const_eval_static_access = const_eval_static_access =
{$kind}s cannot refer to statics {const_eval_const_context}s cannot refer to statics
.help = consider extracting the value of the `static` to a `const`, and referring to that .help = consider extracting the value of the `static` to a `const`, and referring to that
.teach_note = `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. .teach_note = `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.
.teach_help = To fix this, the value can be extracted to a `const` and then used. .teach_help = To fix this, the value can be extracted to a `const` and then used.
@ -47,27 +347,34 @@ const_eval_static_access =
const_eval_thread_local_access = const_eval_thread_local_access =
thread-local statics cannot be accessed at compile-time thread-local statics cannot be accessed at compile-time
const_eval_transient_mut_borrow = mutable references are not allowed in {$kind}s const_eval_thread_local_static =
cannot access thread local static ({$did})
const_eval_too_generic =
encountered overly generic constant
const_eval_too_many_caller_args =
calling a function with more arguments than it expected
const_eval_transient_mut_borrow_raw = raw mutable references are not allowed in {$kind}s const_eval_transient_mut_borrow = mutable references are not allowed in {const_eval_const_context}s
const_eval_unallowed_fn_pointer_call = function pointer calls are not allowed in {$kind}s const_eval_transient_mut_borrow_raw = raw mutable references are not allowed in {const_eval_const_context}s
const_eval_try_block_from_output_non_const =
`try` block cannot convert `{$ty}` to the result in {const_eval_const_context}s
const_eval_unaligned_box = {$front_matter}: encountered an unaligned box (required {$required_bytes} byte alignment but found {$found_bytes})
const_eval_unaligned_ref = {$front_matter}: encountered an unaligned reference (required {$required_bytes} byte alignment but found {$found_bytes})
const_eval_unallowed_fn_pointer_call = function pointer calls are not allowed in {const_eval_const_context}s
const_eval_unallowed_heap_allocations = const_eval_unallowed_heap_allocations =
allocations are not allowed in {$kind}s allocations are not allowed in {const_eval_const_context}s
.label = allocation not allowed in {$kind}s .label = allocation not allowed in {const_eval_const_context}s
.teach_note = .teach_note =
The value of statics and constants must be known at compile time, and they live for the entire lifetime of a program. Creating a boxed value allocates memory on the heap at runtime, and therefore cannot be done at compile time. The value of statics and constants must be known at compile time, and they live for the entire lifetime of a program. Creating a boxed value allocates memory on the heap at runtime, and therefore cannot be done at compile time.
const_eval_unallowed_inline_asm = const_eval_unallowed_inline_asm =
inline assembly is not allowed in {$kind}s inline assembly is not allowed in {const_eval_const_context}s
const_eval_unallowed_mutable_refs = const_eval_unallowed_mutable_refs =
mutable references are not allowed in the final value of {$kind}s mutable references are not allowed in the final value of {const_eval_const_context}s
.teach_note = .teach_note =
References in statics and constants may only refer to immutable values.
Statics are shared everywhere, and if they refer to mutable data one might violate memory Statics are shared everywhere, and if they refer to mutable data one might violate memory
safety since holding multiple mutable references to shared data is not allowed. safety since holding multiple mutable references to shared data is not allowed.
@ -75,7 +382,7 @@ const_eval_unallowed_mutable_refs =
If you really want global mutable state, try using static mut or a global UnsafeCell. If you really want global mutable state, try using static mut or a global UnsafeCell.
const_eval_unallowed_mutable_refs_raw = const_eval_unallowed_mutable_refs_raw =
raw mutable references are not allowed in the final value of {$kind}s raw mutable references are not allowed in the final value of {const_eval_const_context}s
.teach_note = .teach_note =
References in statics and constants may only refer to immutable values. References in statics and constants may only refer to immutable values.
@ -89,9 +396,59 @@ const_eval_unallowed_mutable_refs_raw =
const_eval_unallowed_op_in_const_context = const_eval_unallowed_op_in_const_context =
{$msg} {$msg}
const_eval_undefined_behavior =
it is undefined behavior to use this value
const_eval_undefined_behavior_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.
const_eval_uninhabited_enum_variant_written =
writing discriminant of an uninhabited enum
const_eval_uninhabited_val = {$front_matter}: encountered a value of uninhabited type `{$ty}`
const_eval_uninit = {$front_matter}: encountered uninitialized bytes
const_eval_uninit_bool = {$front_matter}: encountered uninitialized memory, but expected a boolean
const_eval_uninit_box = {$front_matter}: encountered uninitialized memory, but expected a box
const_eval_uninit_char = {$front_matter}: encountered uninitialized memory, but expected a unicode scalar value
const_eval_uninit_enum_tag = {$front_matter}: encountered uninitialized bytes, but expected a valid enum tag
const_eval_uninit_float = {$front_matter}: encountered uninitialized memory, but expected a floating point number
const_eval_uninit_fn_ptr = {$front_matter}: encountered uninitialized memory, but expected a function pointer
const_eval_uninit_init_scalar = {$front_matter}: encountered uninitialized memory, but expected initialized scalar value
const_eval_uninit_int = {$front_matter}: encountered uninitialized memory, but expected an integer
const_eval_uninit_raw_ptr = {$front_matter}: encountered uninitialized memory, but expected a raw pointer
const_eval_uninit_ref = {$front_matter}: encountered uninitialized memory, but expected a reference
const_eval_uninit_str = {$front_matter}: encountered uninitialized data in `str`
const_eval_uninit_unsized_local =
unsized local is used while uninitialized
const_eval_unreachable = entering unreachable code
const_eval_unreachable_unwind =
unwinding past a stack frame that does not allow unwinding
const_eval_unsafe_cell = {$front_matter}: encountered `UnsafeCell` in a `const`
const_eval_unsigned_offset_from_overflow =
`ptr_offset_from_unsigned` called when first pointer has smaller offset than second: {$a_offset} < {$b_offset}
const_eval_unstable_const_fn = `{$def_path}` is not yet stable as a const fn const_eval_unstable_const_fn = `{$def_path}` is not yet stable as a const fn
const_eval_unstable_in_stable = const_eval_unstable_in_stable =
const-stable function cannot use `#[feature({$gate})]` const-stable function cannot use `#[feature({$gate})]`
.unstable_sugg = if it is not part of the public API, make this function unstably const .unstable_sugg = if it is not part of the public API, make this function unstably const
.bypass_sugg = otherwise `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks .bypass_sugg = otherwise `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks
const_eval_unsupported_untyped_pointer = unsupported untyped pointer in constant
.note = memory only reachable via raw pointers is not supported
const_eval_unterminated_c_string =
reading a null-terminated string starting at {$pointer} with no null found before end of allocation
const_eval_unwind_past_top =
unwinding past the topmost frame of the stack
const_eval_upcast_mismatch =
upcast on a pointer whose vtable does not match its type
const_eval_validation_invalid_bool = {$front_matter}: encountered {$value}, but expected a boolean
const_eval_validation_invalid_char = {$front_matter}: encountered {$value}, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`)
const_eval_write_to_read_only =
writing to {$allocation} which is read-only
const_eval_zst_pointer_out_of_bounds =
{$bad_pointer_message}: {$alloc_id} has size {$alloc_size}, so pointer at offset {$ptr_offset} is out-of-bounds

View file

@ -1,17 +1,15 @@
use std::error::Error; use std::mem;
use std::fmt;
use rustc_errors::Diagnostic; use rustc_errors::{DiagnosticArgValue, DiagnosticMessage, IntoDiagnostic, IntoDiagnosticArg};
use rustc_middle::mir::AssertKind; use rustc_middle::mir::AssertKind;
use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::{layout::LayoutError, ConstInt}; use rustc_middle::ty::{layout::LayoutError, ConstInt};
use rustc_span::{Span, Symbol}; use rustc_span::source_map::Spanned;
use rustc_span::{ErrorGuaranteed, Span, Symbol};
use super::InterpCx; use super::InterpCx;
use crate::interpret::{ use crate::errors::{self, FrameNote, ReportErrorExt};
struct_error, ErrorHandled, FrameInfo, InterpError, InterpErrorInfo, Machine, MachineStopType, use crate::interpret::{ErrorHandled, InterpError, InterpErrorInfo, Machine, MachineStopType};
UnsupportedOpInfo,
};
/// The CTFE machine has some custom error kinds. /// The CTFE machine has some custom error kinds.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -23,7 +21,35 @@ pub enum ConstEvalErrKind {
Abort(String), Abort(String),
} }
impl MachineStopType for ConstEvalErrKind {} impl MachineStopType for ConstEvalErrKind {
fn diagnostic_message(&self) -> DiagnosticMessage {
use crate::fluent_generated::*;
use ConstEvalErrKind::*;
match self {
ConstAccessesStatic => const_eval_const_accesses_static,
ModifiedGlobal => const_eval_modified_global,
Panic { .. } => const_eval_panic,
AssertFailure(x) => x.diagnostic_message(),
Abort(msg) => msg.to_string().into(),
}
}
fn add_args(
self: Box<Self>,
adder: &mut dyn FnMut(std::borrow::Cow<'static, str>, DiagnosticArgValue<'static>),
) {
use ConstEvalErrKind::*;
match *self {
ConstAccessesStatic | ModifiedGlobal | Abort(_) => {}
AssertFailure(kind) => kind.add_args(adder),
Panic { msg, line, col, file } => {
adder("msg".into(), msg.into_diagnostic_arg());
adder("file".into(), file.into_diagnostic_arg());
adder("line".into(), line.into_diagnostic_arg());
adder("col".into(), col.into_diagnostic_arg());
}
}
}
}
// The errors become `MachineStop` with plain strings when being raised. // The errors become `MachineStop` with plain strings when being raised.
// `ConstEvalErr` (in `librustc_middle/mir/interpret/error.rs`) knows to // `ConstEvalErr` (in `librustc_middle/mir/interpret/error.rs`) knows to
@ -34,151 +60,117 @@ impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalErrKind {
} }
} }
impl fmt::Display for ConstEvalErrKind { pub fn get_span_and_frames<'tcx, 'mir, M: Machine<'mir, 'tcx>>(
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ecx: &InterpCx<'mir, 'tcx, M>,
use self::ConstEvalErrKind::*; ) -> (Span, Vec<errors::FrameNote>)
match self { where
ConstAccessesStatic => write!(f, "constant accesses static"), 'tcx: 'mir,
ModifiedGlobal => { {
write!(f, "modifying a static's initial value from another static's initializer") let mut stacktrace = ecx.generate_stacktrace();
// Filter out `requires_caller_location` frames.
stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*ecx.tcx));
let span = stacktrace.first().map(|f| f.span).unwrap_or(ecx.tcx.span);
let mut frames = Vec::new();
// Add notes to the backtrace. Don't print a single-line backtrace though.
if stacktrace.len() > 1 {
// Helper closure to print duplicated lines.
let mut add_frame = |mut frame: errors::FrameNote| {
frames.push(errors::FrameNote { times: 0, ..frame.clone() });
// Don't print [... additional calls ...] if the number of lines is small
if frame.times < 3 {
let times = frame.times;
frame.times = 0;
frames.extend(std::iter::repeat(frame).take(times as usize));
} else {
frames.push(frame);
} }
AssertFailure(msg) => write!(f, "{:?}", msg), };
Panic { msg, line, col, file } => {
write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col)
}
Abort(msg) => write!(f, "{}", msg),
}
}
}
impl Error for ConstEvalErrKind {} let mut last_frame: Option<errors::FrameNote> = None;
for frame_info in &stacktrace {
/// When const-evaluation errors, this type is constructed with the resulting information, let frame = frame_info.as_note(*ecx.tcx);
/// and then used to emit the error as a lint or hard error. match last_frame.as_mut() {
#[derive(Debug)] Some(last_frame)
pub(super) struct ConstEvalErr<'tcx> { if last_frame.span == frame.span
pub span: Span, && last_frame.where_ == frame.where_
pub error: InterpError<'tcx>, && last_frame.instance == frame.instance =>
pub stacktrace: Vec<FrameInfo<'tcx>>, {
} last_frame.times += 1;
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 be reported!
pub fn new<'mir, M: Machine<'mir, 'tcx>>(
ecx: &InterpCx<'mir, 'tcx, M>,
error: InterpErrorInfo<'tcx>,
span: Option<Span>,
) -> ConstEvalErr<'tcx>
where
'tcx: 'mir,
{
error.print_backtrace();
let mut stacktrace = ecx.generate_stacktrace();
// Filter out `requires_caller_location` frames.
stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*ecx.tcx));
// If `span` is missing, use topmost remaining frame, or else the "root" span from `ecx.tcx`.
let span = span.or_else(|| stacktrace.first().map(|f| f.span)).unwrap_or(ecx.tcx.span);
ConstEvalErr { error: error.into_kind(), stacktrace, span }
}
pub(super) fn report(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
self.report_decorated(tcx, message, |_| {})
}
#[instrument(level = "trace", skip(self, decorate))]
pub(super) fn decorate(&self, err: &mut Diagnostic, decorate: impl FnOnce(&mut Diagnostic)) {
trace!("reporting const eval failure at {:?}", self.span);
// Add some more context for select error types.
match self.error {
InterpError::Unsupported(
UnsupportedOpInfo::ReadPointerAsBytes
| UnsupportedOpInfo::PartialPointerOverwrite(_)
| UnsupportedOpInfo::PartialPointerCopy(_),
) => {
err.help("this code performed an operation that depends on the underlying bytes representing a pointer");
err.help("the absolute address of a pointer is not known at compile-time, so such operations are not supported");
}
_ => {}
}
// Add spans for the stacktrace. Don't print a single-line backtrace though.
if self.stacktrace.len() > 1 {
// Helper closure to print duplicated lines.
let mut flush_last_line = |last_frame: Option<(String, _)>, times| {
if let Some((line, span)) = last_frame {
err.span_note(span, line.clone());
// Don't print [... additional calls ...] if the number of lines is small
if times < 3 {
for _ in 0..times {
err.span_note(span, line.clone());
}
} else {
err.span_note(
span,
format!("[... {} additional calls {} ...]", times, &line),
);
}
} }
}; Some(last_frame) => {
add_frame(mem::replace(last_frame, frame));
let mut last_frame = None; }
let mut times = 0; None => {
for frame_info in &self.stacktrace {
let frame = (frame_info.to_string(), frame_info.span);
if last_frame.as_ref() == Some(&frame) {
times += 1;
} else {
flush_last_line(last_frame, times);
last_frame = Some(frame); last_frame = Some(frame);
times = 0;
} }
} }
flush_last_line(last_frame, times);
} }
// Let the caller attach any additional information it wants. if let Some(frame) = last_frame {
decorate(err); add_frame(frame);
}
} }
/// Create a diagnostic for this const eval error. (span, frames)
/// }
/// Sets the message passed in via `message` and adds span labels with detailed error
/// information before handing control back to `decorate` to do any final annotations, /// Create a diagnostic for a const eval error.
/// after which the diagnostic is emitted. ///
/// /// This will use the `mk` function for creating the error which will get passed labels according to
/// If `lint_root.is_some()` report it as a lint, else report it as a hard error. /// the `InterpError` and the span and a stacktrace of current execution according to
/// (Except that for some errors, we ignore all that -- see `must_error` below.) /// `get_span_and_frames`.
#[instrument(skip(self, tcx, decorate), level = "debug")] pub(super) fn report<'tcx, C, F, E>(
pub(super) fn report_decorated( tcx: TyCtxt<'tcx>,
&self, error: InterpError<'tcx>,
tcx: TyCtxtAt<'tcx>, span: Option<Span>,
message: &str, get_span_and_frames: C,
decorate: impl FnOnce(&mut Diagnostic), mk: F,
) -> ErrorHandled { ) -> ErrorHandled
debug!("self.error: {:?}", self.error); where
// Special handling for certain errors C: FnOnce() -> (Span, Vec<FrameNote>),
match &self.error { F: FnOnce(Span, Vec<FrameNote>) -> E,
// Don't emit a new diagnostic for these errors E: IntoDiagnostic<'tcx, ErrorGuaranteed>,
err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => { {
ErrorHandled::TooGeneric // Special handling for certain errors
} match error {
err_inval!(AlreadyReported(error_reported)) => ErrorHandled::Reported(*error_reported), // Don't emit a new diagnostic for these errors
err_inval!(Layout(LayoutError::SizeOverflow(_))) => { err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
// We must *always* hard error on these, even if the caller wants just a lint. ErrorHandled::TooGeneric
// The `message` makes little sense here, this is a more serious error than the }
// caller thinks anyway. err_inval!(AlreadyReported(error_reported)) => ErrorHandled::Reported(error_reported),
// See <https://github.com/rust-lang/rust/pull/63152>. err_inval!(Layout(layout_error @ LayoutError::SizeOverflow(_))) => {
let mut err = struct_error(tcx, &self.error.to_string()); // We must *always* hard error on these, even if the caller wants just a lint.
self.decorate(&mut err, decorate); // The `message` makes little sense here, this is a more serious error than the
ErrorHandled::Reported(err.emit().into()) // caller thinks anyway.
} // See <https://github.com/rust-lang/rust/pull/63152>.
_ => { let (our_span, frames) = get_span_and_frames();
// Report as hard error. let span = span.unwrap_or(our_span);
let mut err = struct_error(tcx, message); let mut err =
err.span_label(self.span, self.error.to_string()); tcx.sess.create_err(Spanned { span, node: layout_error.into_diagnostic() });
self.decorate(&mut err, decorate); err.code(rustc_errors::error_code!(E0080));
ErrorHandled::Reported(err.emit().into()) let Some((mut err, handler)) = err.into_diagnostic() else {
panic!("did not emit diag");
};
for frame in frames {
err.eager_subdiagnostic(handler, frame);
} }
ErrorHandled::Reported(handler.emit_diagnostic(&mut err).unwrap().into())
}
_ => {
// Report as hard error.
let (our_span, frames) = get_span_and_frames();
let span = span.unwrap_or(our_span);
let err = mk(span, frames);
let mut err = tcx.sess.create_err(err);
let msg = error.diagnostic_message();
error.add_args(&tcx.sess.parse_sess.span_diagnostic, &mut err);
// Use *our* span to label the interp error
err.span_label(our_span, msg);
ErrorHandled::Reported(err.emit().into())
} }
} }
} }

View file

@ -1,12 +1,12 @@
use crate::const_eval::CheckAlignment; use crate::const_eval::CheckAlignment;
use std::borrow::Cow; use crate::errors::ConstEvalError;
use either::{Left, Right}; use either::{Left, Right};
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_middle::mir; use rustc_middle::mir;
use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::mir::interpret::{ErrorHandled, InterpErrorInfo};
use rustc_middle::mir::pretty::display_allocation; use rustc_middle::mir::pretty::write_allocation_bytes;
use rustc_middle::traits::Reveal; use rustc_middle::traits::Reveal;
use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::print::with_no_trimmed_paths;
@ -14,7 +14,8 @@ use rustc_middle::ty::{self, TyCtxt};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_target::abi::{self, Abi}; use rustc_target::abi::{self, Abi};
use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr}; use super::{CompileTimeEvalContext, CompileTimeInterpreter};
use crate::errors;
use crate::interpret::eval_nullary_intrinsic; use crate::interpret::eval_nullary_intrinsic;
use crate::interpret::{ use crate::interpret::{
intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId, intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId,
@ -22,10 +23,6 @@ use crate::interpret::{
RefTracking, StackPopCleanup, RefTracking, StackPopCleanup,
}; };
const NOTE_ON_UNDEFINED_BEHAVIOR_ERROR: &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 // Returns a pointer to where the result lives
fn eval_body_using_ecx<'mir, 'tcx>( fn eval_body_using_ecx<'mir, 'tcx>(
ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
@ -253,8 +250,14 @@ pub fn eval_to_const_value_raw_provider<'tcx>(
}; };
return eval_nullary_intrinsic(tcx, key.param_env, def_id, substs).map_err(|error| { return eval_nullary_intrinsic(tcx, key.param_env, def_id, substs).map_err(|error| {
let span = tcx.def_span(def_id); let span = tcx.def_span(def_id);
let error = ConstEvalErr { error: error.into_kind(), stacktrace: vec![], span };
error.report(tcx.at(span), "could not evaluate nullary intrinsic") super::report(
tcx,
error.into_kind(),
Some(span),
|| (span, vec![]),
|span, _| errors::NullaryIntrinsicError { span },
)
}); });
} }
@ -318,9 +321,11 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
let res = ecx.load_mir(cid.instance.def, cid.promoted); let res = ecx.load_mir(cid.instance.def, cid.promoted);
match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) { match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) {
Err(error) => { Err(error) => {
let err = ConstEvalErr::new(&ecx, error, None); let (error, backtrace) = error.into_parts();
let msg = if is_static { backtrace.print_backtrace();
Cow::from("could not evaluate static initializer")
let (kind, instance) = if is_static {
("static", String::new())
} else { } else {
// If the current item has generics, we'd like to enrich the message with the // 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 // instance and its substs: to show the actual compile-time values, in addition to
@ -328,19 +333,29 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
let instance = &key.value.instance; let instance = &key.value.instance;
if !instance.substs.is_empty() { if !instance.substs.is_empty() {
let instance = with_no_trimmed_paths!(instance.to_string()); let instance = with_no_trimmed_paths!(instance.to_string());
let msg = format!("evaluation of `{}` failed", instance); ("const_with_path", instance)
Cow::from(msg)
} else { } else {
Cow::from("evaluation of constant value failed") ("const", String::new())
} }
}; };
Err(err.report(ecx.tcx.at(err.span), &msg)) Err(super::report(
*ecx.tcx,
error,
None,
|| super::get_span_and_frames(&ecx),
|span, frames| ConstEvalError {
span,
error_kind: kind,
instance,
frame_notes: frames,
},
))
} }
Ok(mplace) => { Ok(mplace) => {
// Since evaluation had no errors, validate the resulting constant. // Since evaluation had no errors, validate the resulting constant.
// This is a separate `try` block to provide more targeted error reporting. // This is a separate `try` block to provide more targeted error reporting.
let validation = try { let validation: Result<_, InterpErrorInfo<'_>> = try {
let mut ref_tracking = RefTracking::new(mplace); let mut ref_tracking = RefTracking::new(mplace);
let mut inner = false; let mut inner = false;
while let Some((mplace, path)) = ref_tracking.todo.pop() { while let Some((mplace, path)) = ref_tracking.todo.pop() {
@ -357,23 +372,37 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
} }
}; };
let alloc_id = mplace.ptr.provenance.unwrap(); let alloc_id = mplace.ptr.provenance.unwrap();
// Validation failed, report an error. This is always a hard error.
if let Err(error) = validation { if let Err(error) = validation {
// Validation failed, report an error. This is always a hard error. let (error, backtrace) = error.into_parts();
let err = ConstEvalErr::new(&ecx, error, None); backtrace.print_backtrace();
Err(err.report_decorated(
ecx.tcx, let ub_note = matches!(error, InterpError::UndefinedBehavior(_)).then(|| {});
"it is undefined behavior to use this value",
|diag| { let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner();
if matches!(err.error, InterpError::UndefinedBehavior(_)) { let mut bytes = String::new();
diag.note(NOTE_ON_UNDEFINED_BEHAVIOR_ERROR); if alloc.size() != abi::Size::ZERO {
} bytes = "\n".into();
diag.note(format!( // FIXME(translation) there might be pieces that are translatable.
"the raw bytes of the constant ({}", write_allocation_bytes(*ecx.tcx, alloc, &mut bytes, " ").unwrap();
display_allocation( }
*ecx.tcx, let raw_bytes = errors::RawBytesNote {
ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner() size: alloc.size().bytes(),
) align: alloc.align.bytes(),
)); bytes,
};
Err(super::report(
*ecx.tcx,
error,
None,
|| super::get_span_and_frames(&ecx),
move |span, frames| errors::UndefinedBehavior {
span,
ub_note,
frames,
raw_bytes,
}, },
)) ))
} else { } else {

View file

@ -25,6 +25,7 @@ use crate::interpret::{
self, compile_time_machine, AllocId, ConstAllocation, FnVal, Frame, ImmTy, InterpCx, self, compile_time_machine, AllocId, ConstAllocation, FnVal, Frame, ImmTy, InterpCx,
InterpResult, OpTy, PlaceTy, Pointer, Scalar, InterpResult, OpTy, PlaceTy, Pointer, Scalar,
}; };
use crate::{errors, fluent_generated as fluent};
use super::error::*; use super::error::*;
@ -254,7 +255,10 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
let target_align = self.read_scalar(&args[1])?.to_target_usize(self)?; let target_align = self.read_scalar(&args[1])?.to_target_usize(self)?;
if !target_align.is_power_of_two() { if !target_align.is_power_of_two() {
throw_ub_format!("`align_offset` called with non-power-of-two align: {}", target_align); throw_ub_custom!(
fluent::const_eval_align_offset_invalid_align,
target_align = target_align,
);
} }
match self.ptr_try_get_alloc_id(ptr) { match self.ptr_try_get_alloc_id(ptr) {
@ -360,15 +364,18 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
"`alignment_check_failed` called when no alignment check requested" "`alignment_check_failed` called when no alignment check requested"
), ),
CheckAlignment::FutureIncompat => { CheckAlignment::FutureIncompat => {
let err = ConstEvalErr::new(ecx, err, None); let (_, backtrace) = err.into_parts();
ecx.tcx.struct_span_lint_hir( backtrace.print_backtrace();
let (span, frames) = super::get_span_and_frames(&ecx);
ecx.tcx.emit_spanned_lint(
INVALID_ALIGNMENT, INVALID_ALIGNMENT,
ecx.stack().iter().find_map(|frame| frame.lint_root()).unwrap_or(CRATE_HIR_ID), ecx.stack().iter().find_map(|frame| frame.lint_root()).unwrap_or(CRATE_HIR_ID),
err.span, span,
err.error.to_string(), errors::AlignmentCheckFailed {
|db| { has: has.bytes(),
err.decorate(db, |_| {}); required: required.bytes(),
db frames,
}, },
); );
Ok(()) Ok(())
@ -482,7 +489,12 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
let align = match Align::from_bytes(align) { let align = match Align::from_bytes(align) {
Ok(a) => a, Ok(a) => a,
Err(err) => throw_ub_format!("align has to be a power of 2, {}", err), Err(err) => throw_ub_custom!(
fluent::const_eval_invalid_align_details,
name = "const_allocate",
err_kind = err.diag_ident(),
align = err.align()
),
}; };
let ptr = ecx.allocate_ptr( let ptr = ecx.allocate_ptr(
@ -500,7 +512,12 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
let size = Size::from_bytes(size); let size = Size::from_bytes(size);
let align = match Align::from_bytes(align) { let align = match Align::from_bytes(align) {
Ok(a) => a, Ok(a) => a,
Err(err) => throw_ub_format!("align has to be a power of 2, {}", err), Err(err) => throw_ub_custom!(
fluent::const_eval_invalid_align_details,
name = "const_deallocate",
err_kind = err.diag_ident(),
align = err.align()
),
}; };
// If an allocation is created in an another const, // If an allocation is created in an another const,

View file

@ -73,17 +73,8 @@ pub(crate) fn eval_to_valtree<'tcx>(
let global_const_id = cid.display(tcx); let global_const_id = cid.display(tcx);
match err { match err {
ValTreeCreationError::NodesOverflow => { ValTreeCreationError::NodesOverflow => {
let msg = format!( let span = tcx.hir().span_if_local(did);
"maximum number of nodes exceeded in constant {}", tcx.sess.emit_err(MaxNumNodesInConstErr { span, global_const_id });
&global_const_id
);
let mut diag = match tcx.hir().span_if_local(did) {
Some(span) => {
tcx.sess.create_err(MaxNumNodesInConstErr { span, global_const_id })
}
None => tcx.sess.struct_err(msg),
};
diag.emit();
Ok(None) Ok(None)
} }

View file

@ -1,6 +1,24 @@
use rustc_errors::{
DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Handler,
IntoDiagnostic,
};
use rustc_hir::ConstContext; use rustc_hir::ConstContext;
use rustc_macros::{Diagnostic, LintDiagnostic}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::mir::interpret::{
CheckInAllocMsg, ExpectedKind, InterpError, InvalidMetaKind, InvalidProgramInfo, PointerKind,
ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo,
};
use rustc_middle::ty::{self, Ty};
use rustc_span::Span; use rustc_span::Span;
use rustc_target::abi::call::AdjustForForeignAbiError;
use rustc_target::abi::{Size, WrappingRange};
#[derive(Diagnostic)]
#[diag(const_eval_dangling_ptr_in_final)]
pub(crate) struct DanglingPtrInFinal {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(const_eval_unstable_in_stable)] #[diag(const_eval_unstable_in_stable)]
@ -92,7 +110,7 @@ pub(crate) struct TransientMutBorrowErrRaw {
#[diag(const_eval_max_num_nodes_in_const)] #[diag(const_eval_max_num_nodes_in_const)]
pub(crate) struct MaxNumNodesInConstErr { pub(crate) struct MaxNumNodesInConstErr {
#[primary_span] #[primary_span]
pub span: Span, pub span: Option<Span>,
pub global_const_id: String, pub global_const_id: String,
} }
@ -175,6 +193,14 @@ pub(crate) struct UnallowedInlineAsm {
pub kind: ConstContext, pub kind: ConstContext,
} }
#[derive(Diagnostic)]
#[diag(const_eval_unsupported_untyped_pointer)]
#[note]
pub(crate) struct UnsupportedUntypedPointer {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(const_eval_interior_mutable_data_refer, code = "E0492")] #[diag(const_eval_interior_mutable_data_refer, code = "E0492")]
pub(crate) struct InteriorMutableDataRefer { pub(crate) struct InteriorMutableDataRefer {
@ -212,3 +238,631 @@ pub struct LongRunningWarn {
#[help] #[help]
pub item_span: Span, pub item_span: Span,
} }
#[derive(Diagnostic)]
#[diag(const_eval_erroneous_constant)]
pub(crate) struct ErroneousConstUsed {
#[primary_span]
pub span: Span,
}
#[derive(Subdiagnostic)]
#[note(const_eval_non_const_impl)]
pub(crate) struct NonConstImplNote {
#[primary_span]
pub span: Span,
}
#[derive(Subdiagnostic, PartialEq, Eq, Clone)]
#[note(const_eval_frame_note)]
pub struct FrameNote {
#[primary_span]
pub span: Span,
pub times: i32,
pub where_: &'static str,
pub instance: String,
}
#[derive(Subdiagnostic)]
#[note(const_eval_raw_bytes)]
pub struct RawBytesNote {
pub size: u64,
pub align: u64,
pub bytes: String,
}
#[derive(Diagnostic)]
#[diag(const_eval_for_loop_into_iter_non_const, code = "E0015")]
pub struct NonConstForLoopIntoIter<'tcx> {
#[primary_span]
pub span: Span,
pub ty: Ty<'tcx>,
pub kind: ConstContext,
}
#[derive(Diagnostic)]
#[diag(const_eval_question_branch_non_const, code = "E0015")]
pub struct NonConstQuestionBranch<'tcx> {
#[primary_span]
pub span: Span,
pub ty: Ty<'tcx>,
pub kind: ConstContext,
}
#[derive(Diagnostic)]
#[diag(const_eval_question_from_residual_non_const, code = "E0015")]
pub struct NonConstQuestionFromResidual<'tcx> {
#[primary_span]
pub span: Span,
pub ty: Ty<'tcx>,
pub kind: ConstContext,
}
#[derive(Diagnostic)]
#[diag(const_eval_try_block_from_output_non_const, code = "E0015")]
pub struct NonConstTryBlockFromOutput<'tcx> {
#[primary_span]
pub span: Span,
pub ty: Ty<'tcx>,
pub kind: ConstContext,
}
#[derive(Diagnostic)]
#[diag(const_eval_await_non_const, code = "E0015")]
pub struct NonConstAwait<'tcx> {
#[primary_span]
pub span: Span,
pub ty: Ty<'tcx>,
pub kind: ConstContext,
}
#[derive(Diagnostic)]
#[diag(const_eval_closure_non_const, code = "E0015")]
pub struct NonConstClosure {
#[primary_span]
pub span: Span,
pub kind: ConstContext,
#[subdiagnostic]
pub note: Option<NonConstClosureNote>,
}
#[derive(Subdiagnostic)]
pub enum NonConstClosureNote {
#[note(const_eval_closure_fndef_not_const)]
FnDef {
#[primary_span]
span: Span,
},
#[note(const_eval_fn_ptr_call)]
FnPtr,
#[note(const_eval_closure_call)]
Closure,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(const_eval_consider_dereferencing, applicability = "machine-applicable")]
pub struct ConsiderDereferencing {
pub deref: String,
#[suggestion_part(code = "{deref}")]
pub span: Span,
#[suggestion_part(code = "{deref}")]
pub rhs_span: Span,
}
#[derive(Diagnostic)]
#[diag(const_eval_operator_non_const, code = "E0015")]
pub struct NonConstOperator {
#[primary_span]
pub span: Span,
pub kind: ConstContext,
#[subdiagnostic]
pub sugg: Option<ConsiderDereferencing>,
}
#[derive(Diagnostic)]
#[diag(const_eval_deref_coercion_non_const, code = "E0015")]
#[note]
pub struct NonConstDerefCoercion<'tcx> {
#[primary_span]
pub span: Span,
pub ty: Ty<'tcx>,
pub kind: ConstContext,
pub target_ty: Ty<'tcx>,
#[note(const_eval_target_note)]
pub deref_target: Option<Span>,
}
#[derive(Diagnostic)]
#[diag(const_eval_live_drop, code = "E0493")]
pub struct LiveDrop<'tcx> {
#[primary_span]
#[label]
pub span: Span,
pub kind: ConstContext,
pub dropped_ty: Ty<'tcx>,
#[label(const_eval_dropped_at_label)]
pub dropped_at: Option<Span>,
}
#[derive(LintDiagnostic)]
#[diag(const_eval_align_check_failed)]
pub struct AlignmentCheckFailed {
pub has: u64,
pub required: u64,
#[subdiagnostic]
pub frames: Vec<FrameNote>,
}
#[derive(Diagnostic)]
#[diag(const_eval_error, code = "E0080")]
pub struct ConstEvalError {
#[primary_span]
pub span: Span,
/// One of "const", "const_with_path", and "static"
pub error_kind: &'static str,
pub instance: String,
#[subdiagnostic]
pub frame_notes: Vec<FrameNote>,
}
#[derive(Diagnostic)]
#[diag(const_eval_nullary_intrinsic_fail)]
pub struct NullaryIntrinsicError {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(const_eval_undefined_behavior, code = "E0080")]
pub struct UndefinedBehavior {
#[primary_span]
pub span: Span,
#[note(const_eval_undefined_behavior_note)]
pub ub_note: Option<()>,
#[subdiagnostic]
pub frames: Vec<FrameNote>,
#[subdiagnostic]
pub raw_bytes: RawBytesNote,
}
pub trait ReportErrorExt {
/// Returns the diagnostic message for this error.
fn diagnostic_message(&self) -> DiagnosticMessage;
fn add_args<G: EmissionGuarantee>(
self,
handler: &Handler,
builder: &mut DiagnosticBuilder<'_, G>,
);
fn debug(self) -> String
where
Self: Sized,
{
ty::tls::with(move |tcx| {
let mut builder = tcx.sess.struct_allow(DiagnosticMessage::Str(String::new().into()));
let handler = &tcx.sess.parse_sess.span_diagnostic;
let message = self.diagnostic_message();
self.add_args(handler, &mut builder);
let s = handler.eagerly_translate_to_string(message, builder.args());
builder.cancel();
s
})
}
}
fn bad_pointer_message(msg: CheckInAllocMsg, handler: &Handler) -> String {
use crate::fluent_generated::*;
let msg = match msg {
CheckInAllocMsg::DerefTest => const_eval_deref_test,
CheckInAllocMsg::MemoryAccessTest => const_eval_memory_access_test,
CheckInAllocMsg::PointerArithmeticTest => const_eval_pointer_arithmetic_test,
CheckInAllocMsg::OffsetFromTest => const_eval_offset_from_test,
CheckInAllocMsg::InboundsTest => const_eval_in_bounds_test,
};
handler.eagerly_translate_to_string(msg, [].into_iter())
}
impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
fn diagnostic_message(&self) -> DiagnosticMessage {
use crate::fluent_generated::*;
use UndefinedBehaviorInfo::*;
match self {
Ub(msg) => msg.clone().into(),
Unreachable => const_eval_unreachable,
BoundsCheckFailed { .. } => const_eval_bounds_check_failed,
DivisionByZero => const_eval_division_by_zero,
RemainderByZero => const_eval_remainder_by_zero,
DivisionOverflow => const_eval_division_overflow,
RemainderOverflow => const_eval_remainder_overflow,
PointerArithOverflow => const_eval_pointer_arithmetic_overflow,
InvalidMeta(InvalidMetaKind::SliceTooBig) => const_eval_invalid_meta_slice,
InvalidMeta(InvalidMetaKind::TooBig) => const_eval_invalid_meta,
UnterminatedCString(_) => const_eval_unterminated_c_string,
PointerUseAfterFree(_) => const_eval_pointer_use_after_free,
PointerOutOfBounds { ptr_size: Size::ZERO, .. } => const_eval_zst_pointer_out_of_bounds,
PointerOutOfBounds { .. } => const_eval_pointer_out_of_bounds,
DanglingIntPointer(0, _) => const_eval_dangling_null_pointer,
DanglingIntPointer(_, _) => const_eval_dangling_int_pointer,
AlignmentCheckFailed { .. } => const_eval_alignment_check_failed,
WriteToReadOnly(_) => const_eval_write_to_read_only,
DerefFunctionPointer(_) => const_eval_deref_function_pointer,
DerefVTablePointer(_) => const_eval_deref_vtable_pointer,
InvalidBool(_) => const_eval_invalid_bool,
InvalidChar(_) => const_eval_invalid_char,
InvalidTag(_) => const_eval_invalid_tag,
InvalidFunctionPointer(_) => const_eval_invalid_function_pointer,
InvalidVTablePointer(_) => const_eval_invalid_vtable_pointer,
InvalidStr(_) => const_eval_invalid_str,
InvalidUninitBytes(None) => const_eval_invalid_uninit_bytes_unknown,
InvalidUninitBytes(Some(_)) => const_eval_invalid_uninit_bytes,
DeadLocal => const_eval_dead_local,
ScalarSizeMismatch(_) => const_eval_scalar_size_mismatch,
UninhabitedEnumVariantWritten => const_eval_uninhabited_enum_variant_written,
Validation(e) => e.diagnostic_message(),
Custom(x) => (x.msg)(),
}
}
fn add_args<G: EmissionGuarantee>(
self,
handler: &Handler,
builder: &mut DiagnosticBuilder<'_, G>,
) {
use UndefinedBehaviorInfo::*;
match self {
Ub(_)
| Unreachable
| DivisionByZero
| RemainderByZero
| DivisionOverflow
| RemainderOverflow
| PointerArithOverflow
| InvalidMeta(InvalidMetaKind::SliceTooBig)
| InvalidMeta(InvalidMetaKind::TooBig)
| InvalidUninitBytes(None)
| DeadLocal
| UninhabitedEnumVariantWritten => {}
BoundsCheckFailed { len, index } => {
builder.set_arg("len", len);
builder.set_arg("index", index);
}
UnterminatedCString(ptr) | InvalidFunctionPointer(ptr) | InvalidVTablePointer(ptr) => {
builder.set_arg("pointer", ptr);
}
PointerUseAfterFree(allocation) => {
builder.set_arg("allocation", allocation);
}
PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size, msg } => {
builder
.set_arg("alloc_id", alloc_id)
.set_arg("alloc_size", alloc_size.bytes())
.set_arg("ptr_offset", ptr_offset)
.set_arg("ptr_size", ptr_size.bytes())
.set_arg("bad_pointer_message", bad_pointer_message(msg, handler));
}
DanglingIntPointer(ptr, msg) => {
if ptr != 0 {
builder.set_arg("pointer", format!("{ptr:#x}[noalloc]"));
}
builder.set_arg("bad_pointer_message", bad_pointer_message(msg, handler));
}
AlignmentCheckFailed { required, has } => {
builder.set_arg("required", required.bytes());
builder.set_arg("has", has.bytes());
}
WriteToReadOnly(alloc) | DerefFunctionPointer(alloc) | DerefVTablePointer(alloc) => {
builder.set_arg("allocation", alloc);
}
InvalidBool(b) => {
builder.set_arg("value", format!("{b:02x}"));
}
InvalidChar(c) => {
builder.set_arg("value", format!("{c:08x}"));
}
InvalidTag(tag) => {
builder.set_arg("tag", format!("{tag:x}"));
}
InvalidStr(err) => {
builder.set_arg("err", format!("{err}"));
}
InvalidUninitBytes(Some((alloc, info))) => {
builder.set_arg("alloc", alloc);
builder.set_arg("access", info.access);
builder.set_arg("uninit", info.uninit);
}
ScalarSizeMismatch(info) => {
builder.set_arg("target_size", info.target_size);
builder.set_arg("data_size", info.data_size);
}
Validation(e) => e.add_args(handler, builder),
Custom(custom) => {
(custom.add_args)(&mut |name, value| {
builder.set_arg(name, value);
});
}
}
}
}
impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
fn diagnostic_message(&self) -> DiagnosticMessage {
use crate::fluent_generated::*;
use rustc_middle::mir::interpret::ValidationErrorKind::*;
match self.kind {
PtrToUninhabited { ptr_kind: PointerKind::Box, .. } => const_eval_box_to_uninhabited,
PtrToUninhabited { ptr_kind: PointerKind::Ref, .. } => const_eval_ref_to_uninhabited,
PtrToStatic { ptr_kind: PointerKind::Box } => const_eval_box_to_static,
PtrToStatic { ptr_kind: PointerKind::Ref } => const_eval_ref_to_static,
PtrToMut { ptr_kind: PointerKind::Box } => const_eval_box_to_mut,
PtrToMut { ptr_kind: PointerKind::Ref } => const_eval_ref_to_mut,
ExpectedNonPtr { .. } => const_eval_expected_non_ptr,
MutableRefInConst => const_eval_mutable_ref_in_const,
NullFnPtr => const_eval_null_fn_ptr,
NeverVal => const_eval_never_val,
NullablePtrOutOfRange { .. } => const_eval_nullable_ptr_out_of_range,
PtrOutOfRange { .. } => const_eval_ptr_out_of_range,
OutOfRange { .. } => const_eval_out_of_range,
UnsafeCell => const_eval_unsafe_cell,
UninhabitedVal { .. } => const_eval_uninhabited_val,
InvalidEnumTag { .. } => const_eval_invalid_enum_tag,
UninitEnumTag => const_eval_uninit_enum_tag,
UninitStr => const_eval_uninit_str,
Uninit { expected: ExpectedKind::Bool } => const_eval_uninit_bool,
Uninit { expected: ExpectedKind::Reference } => const_eval_uninit_ref,
Uninit { expected: ExpectedKind::Box } => const_eval_uninit_box,
Uninit { expected: ExpectedKind::RawPtr } => const_eval_uninit_raw_ptr,
Uninit { expected: ExpectedKind::InitScalar } => const_eval_uninit_init_scalar,
Uninit { expected: ExpectedKind::Char } => const_eval_uninit_char,
Uninit { expected: ExpectedKind::Float } => const_eval_uninit_float,
Uninit { expected: ExpectedKind::Int } => const_eval_uninit_int,
Uninit { expected: ExpectedKind::FnPtr } => const_eval_uninit_fn_ptr,
UninitVal => const_eval_uninit,
InvalidVTablePtr { .. } => const_eval_invalid_vtable_ptr,
InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Box } => {
const_eval_invalid_box_slice_meta
}
InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Ref } => {
const_eval_invalid_ref_slice_meta
}
InvalidMetaTooLarge { ptr_kind: PointerKind::Box } => const_eval_invalid_box_meta,
InvalidMetaTooLarge { ptr_kind: PointerKind::Ref } => const_eval_invalid_ref_meta,
UnalignedPtr { ptr_kind: PointerKind::Ref, .. } => const_eval_unaligned_ref,
UnalignedPtr { ptr_kind: PointerKind::Box, .. } => const_eval_unaligned_box,
NullPtr { ptr_kind: PointerKind::Box } => const_eval_null_box,
NullPtr { ptr_kind: PointerKind::Ref } => const_eval_null_ref,
DanglingPtrNoProvenance { ptr_kind: PointerKind::Box, .. } => {
const_eval_dangling_box_no_provenance
}
DanglingPtrNoProvenance { ptr_kind: PointerKind::Ref, .. } => {
const_eval_dangling_ref_no_provenance
}
DanglingPtrOutOfBounds { ptr_kind: PointerKind::Box } => {
const_eval_dangling_box_out_of_bounds
}
DanglingPtrOutOfBounds { ptr_kind: PointerKind::Ref } => {
const_eval_dangling_ref_out_of_bounds
}
DanglingPtrUseAfterFree { ptr_kind: PointerKind::Box } => {
const_eval_dangling_box_use_after_free
}
DanglingPtrUseAfterFree { ptr_kind: PointerKind::Ref } => {
const_eval_dangling_ref_use_after_free
}
InvalidBool { .. } => const_eval_validation_invalid_bool,
InvalidChar { .. } => const_eval_validation_invalid_char,
InvalidFnPtr { .. } => const_eval_invalid_fn_ptr,
}
}
fn add_args<G: EmissionGuarantee>(self, handler: &Handler, err: &mut DiagnosticBuilder<'_, G>) {
use crate::fluent_generated as fluent;
use rustc_middle::mir::interpret::ValidationErrorKind::*;
let message = if let Some(path) = self.path {
handler.eagerly_translate_to_string(
fluent::const_eval_invalid_value_with_path,
[("path".into(), DiagnosticArgValue::Str(path.into()))].iter().map(|(a, b)| (a, b)),
)
} else {
handler.eagerly_translate_to_string(fluent::const_eval_invalid_value, [].into_iter())
};
err.set_arg("front_matter", message);
fn add_range_arg<G: EmissionGuarantee>(
r: WrappingRange,
max_hi: u128,
handler: &Handler,
err: &mut DiagnosticBuilder<'_, G>,
) {
let WrappingRange { start: lo, end: hi } = r;
assert!(hi <= max_hi);
let msg = if lo > hi {
fluent::const_eval_range_wrapping
} else if lo == hi {
fluent::const_eval_range_singular
} else if lo == 0 {
assert!(hi < max_hi, "should not be printing if the range covers everything");
fluent::const_eval_range_upper
} else if hi == max_hi {
assert!(lo > 0, "should not be printing if the range covers everything");
fluent::const_eval_range_lower
} else {
fluent::const_eval_range
};
let args = [
("lo".into(), DiagnosticArgValue::Str(lo.to_string().into())),
("hi".into(), DiagnosticArgValue::Str(hi.to_string().into())),
];
let args = args.iter().map(|(a, b)| (a, b));
let message = handler.eagerly_translate_to_string(msg, args);
err.set_arg("in_range", message);
}
match self.kind {
PtrToUninhabited { ty, .. } | UninhabitedVal { ty } => {
err.set_arg("ty", ty);
}
ExpectedNonPtr { value }
| InvalidEnumTag { value }
| InvalidVTablePtr { value }
| InvalidBool { value }
| InvalidChar { value }
| InvalidFnPtr { value } => {
err.set_arg("value", value);
}
NullablePtrOutOfRange { range, max_value } | PtrOutOfRange { range, max_value } => {
add_range_arg(range, max_value, handler, err)
}
OutOfRange { range, max_value, value } => {
err.set_arg("value", value);
add_range_arg(range, max_value, handler, err);
}
UnalignedPtr { required_bytes, found_bytes, .. } => {
err.set_arg("required_bytes", required_bytes);
err.set_arg("found_bytes", found_bytes);
}
DanglingPtrNoProvenance { pointer, .. } => {
err.set_arg("pointer", pointer);
}
NullPtr { .. }
| PtrToStatic { .. }
| PtrToMut { .. }
| MutableRefInConst
| NullFnPtr
| NeverVal
| UnsafeCell
| UninitEnumTag
| UninitStr
| Uninit { .. }
| UninitVal
| InvalidMetaSliceTooLarge { .. }
| InvalidMetaTooLarge { .. }
| DanglingPtrUseAfterFree { .. }
| DanglingPtrOutOfBounds { .. } => {}
}
}
}
impl ReportErrorExt for UnsupportedOpInfo {
fn diagnostic_message(&self) -> DiagnosticMessage {
use crate::fluent_generated::*;
match self {
UnsupportedOpInfo::Unsupported(s) => s.clone().into(),
UnsupportedOpInfo::PartialPointerOverwrite(_) => const_eval_partial_pointer_overwrite,
UnsupportedOpInfo::PartialPointerCopy(_) => const_eval_partial_pointer_copy,
UnsupportedOpInfo::ReadPointerAsBytes => const_eval_read_pointer_as_bytes,
UnsupportedOpInfo::ThreadLocalStatic(_) => const_eval_thread_local_static,
UnsupportedOpInfo::ReadExternStatic(_) => const_eval_read_extern_static,
}
}
fn add_args<G: EmissionGuarantee>(self, _: &Handler, builder: &mut DiagnosticBuilder<'_, G>) {
use crate::fluent_generated::*;
use UnsupportedOpInfo::*;
if let ReadPointerAsBytes | PartialPointerOverwrite(_) | PartialPointerCopy(_) = self {
builder.help(const_eval_ptr_as_bytes_1);
builder.help(const_eval_ptr_as_bytes_2);
}
match self {
Unsupported(_) | ReadPointerAsBytes => {}
PartialPointerOverwrite(ptr) | PartialPointerCopy(ptr) => {
builder.set_arg("ptr", ptr);
}
ThreadLocalStatic(did) | ReadExternStatic(did) => {
builder.set_arg("did", format!("{did:?}"));
}
}
}
}
impl<'tcx> ReportErrorExt for InterpError<'tcx> {
fn diagnostic_message(&self) -> DiagnosticMessage {
match self {
InterpError::UndefinedBehavior(ub) => ub.diagnostic_message(),
InterpError::Unsupported(e) => e.diagnostic_message(),
InterpError::InvalidProgram(e) => e.diagnostic_message(),
InterpError::ResourceExhaustion(e) => e.diagnostic_message(),
InterpError::MachineStop(e) => e.diagnostic_message(),
}
}
fn add_args<G: EmissionGuarantee>(
self,
handler: &Handler,
builder: &mut DiagnosticBuilder<'_, G>,
) {
match self {
InterpError::UndefinedBehavior(ub) => ub.add_args(handler, builder),
InterpError::Unsupported(e) => e.add_args(handler, builder),
InterpError::InvalidProgram(e) => e.add_args(handler, builder),
InterpError::ResourceExhaustion(e) => e.add_args(handler, builder),
InterpError::MachineStop(e) => e.add_args(&mut |name, value| {
builder.set_arg(name, value);
}),
}
}
}
impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> {
fn diagnostic_message(&self) -> DiagnosticMessage {
use crate::fluent_generated::*;
match self {
InvalidProgramInfo::TooGeneric => const_eval_too_generic,
InvalidProgramInfo::AlreadyReported(_) => const_eval_already_reported,
InvalidProgramInfo::Layout(e) => e.diagnostic_message(),
InvalidProgramInfo::FnAbiAdjustForForeignAbi(_) => {
rustc_middle::error::middle_adjust_for_foreign_abi_error
}
InvalidProgramInfo::SizeOfUnsizedType(_) => const_eval_size_of_unsized,
InvalidProgramInfo::UninitUnsizedLocal => const_eval_uninit_unsized_local,
}
}
fn add_args<G: EmissionGuarantee>(
self,
handler: &Handler,
builder: &mut DiagnosticBuilder<'_, G>,
) {
match self {
InvalidProgramInfo::TooGeneric
| InvalidProgramInfo::AlreadyReported(_)
| InvalidProgramInfo::UninitUnsizedLocal => {}
InvalidProgramInfo::Layout(e) => {
let diag: DiagnosticBuilder<'_, ()> = e.into_diagnostic().into_diagnostic(handler);
for (name, val) in diag.args() {
builder.set_arg(name.clone(), val.clone());
}
diag.cancel();
}
InvalidProgramInfo::FnAbiAdjustForForeignAbi(
AdjustForForeignAbiError::Unsupported { arch, abi },
) => {
builder.set_arg("arch", arch);
builder.set_arg("abi", abi.name());
}
InvalidProgramInfo::SizeOfUnsizedType(ty) => {
builder.set_arg("ty", ty);
}
}
}
}
impl ReportErrorExt for ResourceExhaustionInfo {
fn diagnostic_message(&self) -> DiagnosticMessage {
use crate::fluent_generated::*;
match self {
ResourceExhaustionInfo::StackFrameLimitReached => const_eval_stack_frame_limit_reached,
ResourceExhaustionInfo::MemoryExhausted => const_eval_memory_exhausted,
ResourceExhaustionInfo::AddressSpaceFull => const_eval_address_space_full,
}
}
fn add_args<G: EmissionGuarantee>(self, _: &Handler, _: &mut DiagnosticBuilder<'_, G>) {}
}

View file

@ -14,6 +14,8 @@ use super::{
util::ensure_monomorphic_enough, FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy, util::ensure_monomorphic_enough, FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy,
}; };
use crate::fluent_generated as fluent;
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
pub fn cast( pub fn cast(
&mut self, &mut self,
@ -138,12 +140,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
assert!(src.layout.is_sized()); assert!(src.layout.is_sized());
assert!(dest.layout.is_sized()); assert!(dest.layout.is_sized());
if src.layout.size != dest.layout.size { if src.layout.size != dest.layout.size {
throw_ub_format!( let src_bytes = src.layout.size.bytes();
"transmuting from {}-byte type to {}-byte type: `{}` -> `{}`", let dest_bytes = dest.layout.size.bytes();
src.layout.size.bytes(), let src_ty = format!("{}", src.layout.ty);
dest.layout.size.bytes(), let dest_ty = format!("{}", dest.layout.ty);
src.layout.ty, throw_ub_custom!(
dest.layout.ty, fluent::const_eval_invalid_transmute,
src_bytes = src_bytes,
dest_bytes = dest_bytes,
src = src_ty,
dest = dest_ty,
); );
} }
@ -363,7 +369,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let old_vptr = old_vptr.to_pointer(self)?; let old_vptr = old_vptr.to_pointer(self)?;
let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?; let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?;
if old_trait != data_a.principal() { if old_trait != data_a.principal() {
throw_ub_format!("upcast on a pointer whose vtable does not match its type"); throw_ub_custom!(fluent::const_eval_upcast_mismatch);
} }
let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?; let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?;
self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)

View file

@ -1,6 +1,5 @@
use std::cell::Cell; use std::cell::Cell;
use std::fmt; use std::{fmt, mem};
use std::mem;
use either::{Either, Left, Right}; use either::{Either, Left, Right};
@ -8,7 +7,7 @@ use hir::CRATE_HIR_ID;
use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData}; use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
use rustc_index::IndexVec; use rustc_index::IndexVec;
use rustc_middle::mir; use rustc_middle::mir;
use rustc_middle::mir::interpret::{ErrorHandled, InterpError, ReportedErrorInfo}; use rustc_middle::mir::interpret::{ErrorHandled, InterpError, InvalidMetaKind, ReportedErrorInfo};
use rustc_middle::query::TyCtxtAt; use rustc_middle::query::TyCtxtAt;
use rustc_middle::ty::layout::{ use rustc_middle::ty::layout::{
self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers, self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers,
@ -25,6 +24,8 @@ use super::{
MemPlaceMeta, Memory, MemoryKind, Operand, Place, PlaceTy, PointerArithmetic, Provenance, MemPlaceMeta, Memory, MemoryKind, Operand, Place, PlaceTy, PointerArithmetic, Provenance,
Scalar, StackPopJump, Scalar, StackPopJump,
}; };
use crate::errors::{self, ErroneousConstUsed};
use crate::fluent_generated as fluent;
use crate::util; use crate::util;
pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> { pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
@ -247,6 +248,7 @@ impl<'mir, 'tcx, Prov: Provenance, Extra> Frame<'mir, 'tcx, Prov, Extra> {
} }
} }
// FIXME: only used by miri, should be removed once translatable.
impl<'tcx> fmt::Display for FrameInfo<'tcx> { impl<'tcx> fmt::Display for FrameInfo<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
ty::tls::with(|tcx| { ty::tls::with(|tcx| {
@ -264,6 +266,21 @@ impl<'tcx> fmt::Display for FrameInfo<'tcx> {
} }
} }
impl<'tcx> FrameInfo<'tcx> {
pub fn as_note(&self, tcx: TyCtxt<'tcx>) -> errors::FrameNote {
let span = self.span;
if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr {
errors::FrameNote { where_: "closure", span, instance: String::new(), times: 0 }
} else {
let instance = format!("{}", self.instance);
// 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.
errors::FrameNote { where_: "instance", span, instance, times: 0 }
}
}
}
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> { impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> {
#[inline] #[inline]
fn data_layout(&self) -> &TargetDataLayout { fn data_layout(&self) -> &TargetDataLayout {
@ -620,7 +637,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Check if this brought us over the size limit. // Check if this brought us over the size limit.
if size > self.max_size_of_val() { if size > self.max_size_of_val() {
throw_ub!(InvalidMeta("total size is bigger than largest supported object")); throw_ub!(InvalidMeta(InvalidMetaKind::TooBig));
} }
Ok(Some((size, align))) Ok(Some((size, align)))
} }
@ -638,7 +655,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let size = elem.size.bytes().saturating_mul(len); // we rely on `max_size_of_val` being smaller than `u64::MAX`. let size = elem.size.bytes().saturating_mul(len); // we rely on `max_size_of_val` being smaller than `u64::MAX`.
let size = Size::from_bytes(size); let size = Size::from_bytes(size);
if size > self.max_size_of_val() { if size > self.max_size_of_val() {
throw_ub!(InvalidMeta("slice is bigger than largest supported object")); throw_ub!(InvalidMeta(InvalidMetaKind::SliceTooBig));
} }
Ok(Some((size, elem.align.abi))) Ok(Some((size, elem.align.abi)))
} }
@ -746,7 +763,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
mir::UnwindAction::Cleanup(block) => Left(mir::Location { block, statement_index: 0 }), mir::UnwindAction::Cleanup(block) => Left(mir::Location { block, statement_index: 0 }),
mir::UnwindAction::Continue => Right(self.frame_mut().body.span), mir::UnwindAction::Continue => Right(self.frame_mut().body.span),
mir::UnwindAction::Unreachable => { mir::UnwindAction::Unreachable => {
throw_ub_format!("unwinding past a stack frame that does not allow unwinding") throw_ub_custom!(fluent::const_eval_unreachable_unwind);
} }
mir::UnwindAction::Terminate => { mir::UnwindAction::Terminate => {
self.frame_mut().loc = Right(self.frame_mut().body.span); self.frame_mut().loc = Right(self.frame_mut().body.span);
@ -785,7 +802,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
} }
); );
if unwinding && self.frame_idx() == 0 { if unwinding && self.frame_idx() == 0 {
throw_ub_format!("unwinding past the topmost frame of the stack"); throw_ub_custom!(fluent::const_eval_unwind_past_top);
} }
// Copy return value. Must of course happen *before* we deallocate the locals. // Copy return value. Must of course happen *before* we deallocate the locals.
@ -873,7 +890,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// StorageLive expects the local to be dead, and marks it live. // StorageLive expects the local to be dead, and marks it live.
let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val); let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val);
if !matches!(old, LocalValue::Dead) { if !matches!(old, LocalValue::Dead) {
throw_ub_format!("StorageLive on a local that was already live"); throw_ub_custom!(fluent::const_eval_double_storage_live);
} }
Ok(()) Ok(())
} }
@ -916,7 +933,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
ErrorHandled::Reported(err) => { ErrorHandled::Reported(err) => {
if !err.is_tainted_by_errors() && let Some(span) = span { if !err.is_tainted_by_errors() && let Some(span) = span {
// To make it easier to figure out where this error comes from, also add a note at the current location. // To make it easier to figure out where this error comes from, also add a note at the current location.
self.tcx.sess.span_note_without_error(span, "erroneous constant used"); self.tcx.sess.emit_note(ErroneousConstUsed { span });
} }
err_inval!(AlreadyReported(err)) err_inval!(AlreadyReported(err))
} }

View file

@ -28,6 +28,7 @@ use super::{
ValueVisitor, ValueVisitor,
}; };
use crate::const_eval; use crate::const_eval;
use crate::errors::{DanglingPtrInFinal, UnsupportedUntypedPointer};
pub trait CompileTimeMachine<'mir, 'tcx, T> = Machine< pub trait CompileTimeMachine<'mir, 'tcx, T> = Machine<
'mir, 'mir,
@ -320,10 +321,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
} }
} }
/// How a constant value should be interned.
#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
pub enum InternKind { pub enum InternKind {
/// The `mutability` of the static, ignoring the type which may have interior mutability. /// The `mutability` of the static, ignoring the type which may have interior mutability.
Static(hir::Mutability), Static(hir::Mutability),
/// A `const` item
Constant, Constant,
Promoted, Promoted,
} }
@ -388,8 +391,7 @@ pub fn intern_const_alloc_recursive<
ecx.tcx.sess.delay_span_bug( ecx.tcx.sess.delay_span_bug(
ecx.tcx.span, ecx.tcx.span,
format!( format!(
"error during interning should later cause validation failure: {}", "error during interning should later cause validation failure: {error:?}"
error
), ),
); );
} }
@ -425,14 +427,16 @@ pub fn intern_const_alloc_recursive<
// immutability is so important. // immutability is so important.
alloc.mutability = Mutability::Not; alloc.mutability = Mutability::Not;
} }
// If it's a constant, we should not have any "leftovers" as everything
// is tracked by const-checking.
// FIXME: downgrade this to a warning? It rejects some legitimate consts,
// such as `const CONST_RAW: *const Vec<i32> = &Vec::new() as *const _;`.
//
// NOTE: it looks likes this code path is only reachable when we try to intern
// something that cannot be promoted, which in constants means values that have
// drop glue, such as the example above.
InternKind::Constant => { InternKind::Constant => {
// If it's a constant, we should not have any "leftovers" as everything ecx.tcx.sess.emit_err(UnsupportedUntypedPointer { span: ecx.tcx.span });
// is tracked by const-checking.
// FIXME: downgrade this to a warning? It rejects some legitimate consts,
// such as `const CONST_RAW: *const Vec<i32> = &Vec::new() as *const _;`.
ecx.tcx
.sess
.span_err(ecx.tcx.span, "untyped pointers are not allowed in constant");
// For better errors later, mark the allocation as immutable. // For better errors later, mark the allocation as immutable.
alloc.mutability = Mutability::Not; alloc.mutability = Mutability::Not;
} }
@ -447,10 +451,7 @@ pub fn intern_const_alloc_recursive<
} else if ecx.memory.dead_alloc_map.contains_key(&alloc_id) { } else if ecx.memory.dead_alloc_map.contains_key(&alloc_id) {
// Codegen does not like dangling pointers, and generally `tcx` assumes that // Codegen does not like dangling pointers, and generally `tcx` assumes that
// all allocations referenced anywhere actually exist. So, make sure we error here. // all allocations referenced anywhere actually exist. So, make sure we error here.
let reported = ecx let reported = ecx.tcx.sess.emit_err(DanglingPtrInFinal { span: ecx.tcx.span });
.tcx
.sess
.span_err(ecx.tcx.span, "encountered dangling pointer in final constant");
return Err(reported); return Err(reported);
} else if ecx.tcx.try_get_global_alloc(alloc_id).is_none() { } else if ecx.tcx.try_get_global_alloc(alloc_id).is_none() {
// We have hit an `AllocId` that is neither in local or global memory and isn't // We have hit an `AllocId` that is neither in local or global memory and isn't

View file

@ -22,6 +22,8 @@ use super::{
Pointer, Pointer,
}; };
use crate::fluent_generated as fluent;
mod caller_location; mod caller_location;
fn numeric_intrinsic<Prov>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Prov> { fn numeric_intrinsic<Prov>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Prov> {
@ -198,15 +200,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
ty ty
), ),
}; };
let (nonzero, intrinsic_name) = match intrinsic_name { let (nonzero, actual_intrinsic_name) = match intrinsic_name {
sym::cttz_nonzero => (true, sym::cttz), sym::cttz_nonzero => (true, sym::cttz),
sym::ctlz_nonzero => (true, sym::ctlz), sym::ctlz_nonzero => (true, sym::ctlz),
other => (false, other), other => (false, other),
}; };
if nonzero && bits == 0 { if nonzero && bits == 0 {
throw_ub_format!("`{}_nonzero` called on 0", intrinsic_name); throw_ub_custom!(
fluent::const_eval_call_nonzero_intrinsic,
name = intrinsic_name,
);
} }
let out_val = numeric_intrinsic(intrinsic_name, bits, kind); let out_val = numeric_intrinsic(actual_intrinsic_name, bits, kind);
self.write_scalar(out_val, dest)?; self.write_scalar(out_val, dest)?;
} }
sym::saturating_add | sym::saturating_sub => { sym::saturating_add | sym::saturating_sub => {
@ -253,9 +258,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let layout = self.layout_of(substs.type_at(0))?; let layout = self.layout_of(substs.type_at(0))?;
let r_val = r.to_scalar().to_bits(layout.size)?; let r_val = r.to_scalar().to_bits(layout.size)?;
if let sym::unchecked_shl | sym::unchecked_shr = intrinsic_name { if let sym::unchecked_shl | sym::unchecked_shr = intrinsic_name {
throw_ub_format!("overflowing shift by {} in `{}`", r_val, intrinsic_name); throw_ub_custom!(
fluent::const_eval_overflow_shift,
val = r_val,
name = intrinsic_name
);
} else { } else {
throw_ub_format!("overflow executing `{}`", intrinsic_name); throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
} }
} }
self.write_scalar(val, dest)?; self.write_scalar(val, dest)?;
@ -314,17 +323,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
(Err(_), _) | (_, Err(_)) => { (Err(_), _) | (_, Err(_)) => {
// We managed to find a valid allocation for one pointer, but not the other. // We managed to find a valid allocation for one pointer, but not the other.
// That means they are definitely not pointing to the same allocation. // That means they are definitely not pointing to the same allocation.
throw_ub_format!( throw_ub_custom!(
"`{}` called on pointers into different allocations", fluent::const_eval_different_allocations,
intrinsic_name name = intrinsic_name,
); );
} }
(Ok((a_alloc_id, a_offset, _)), Ok((b_alloc_id, b_offset, _))) => { (Ok((a_alloc_id, a_offset, _)), Ok((b_alloc_id, b_offset, _))) => {
// Found allocation for both. They must be into the same allocation. // Found allocation for both. They must be into the same allocation.
if a_alloc_id != b_alloc_id { if a_alloc_id != b_alloc_id {
throw_ub_format!( throw_ub_custom!(
"`{}` called on pointers into different allocations", fluent::const_eval_different_allocations,
intrinsic_name name = intrinsic_name,
); );
} }
// Use these offsets for distance calculation. // Use these offsets for distance calculation.
@ -344,11 +353,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
if overflowed { if overflowed {
// a < b // a < b
if intrinsic_name == sym::ptr_offset_from_unsigned { if intrinsic_name == sym::ptr_offset_from_unsigned {
throw_ub_format!( throw_ub_custom!(
"`{}` called when first pointer has smaller offset than second: {} < {}", fluent::const_eval_unsigned_offset_from_overflow,
intrinsic_name, a_offset = a_offset,
a_offset, b_offset = b_offset,
b_offset,
); );
} }
// The signed form of the intrinsic allows this. If we interpret the // The signed form of the intrinsic allows this. If we interpret the
@ -356,9 +364,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// seems *positive*, they were more than isize::MAX apart. // seems *positive*, they were more than isize::MAX apart.
let dist = val.to_target_isize(self)?; let dist = val.to_target_isize(self)?;
if dist >= 0 { if dist >= 0 {
throw_ub_format!( throw_ub_custom!(
"`{}` called when first pointer is too far before second", fluent::const_eval_offset_from_underflow,
intrinsic_name name = intrinsic_name,
); );
} }
dist dist
@ -368,9 +376,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// If converting to isize produced a *negative* result, we had an overflow // If converting to isize produced a *negative* result, we had an overflow
// because they were more than isize::MAX apart. // because they were more than isize::MAX apart.
if dist < 0 { if dist < 0 {
throw_ub_format!( throw_ub_custom!(
"`{}` called when first pointer is too far ahead of second", fluent::const_eval_offset_from_overflow,
intrinsic_name name = intrinsic_name,
); );
} }
dist dist
@ -513,7 +521,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let op = self.eval_operand(op, None)?; let op = self.eval_operand(op, None)?;
let cond = self.read_scalar(&op)?.to_bool()?; let cond = self.read_scalar(&op)?.to_bool()?;
if !cond { if !cond {
throw_ub_format!("`assume` called with `false`"); throw_ub_custom!(fluent::const_eval_assume_false);
} }
Ok(()) Ok(())
} }
@ -542,7 +550,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let (res, overflow, _ty) = self.overflowing_binary_op(BinOp::Rem, &a, &b)?; let (res, overflow, _ty) = self.overflowing_binary_op(BinOp::Rem, &a, &b)?;
assert!(!overflow); // All overflow is UB, so this should never return on overflow. assert!(!overflow); // All overflow is UB, so this should never return on overflow.
if res.assert_bits(a.layout.size) != 0 { if res.assert_bits(a.layout.size) != 0 {
throw_ub_format!("exact_div: {} cannot be divided by {} without remainder", a, b) throw_ub_custom!(
fluent::const_eval_exact_div_has_remainder,
a = format!("{a}"),
b = format!("{b}")
)
} }
// `Rem` says this is all right, so we can let `Div` do its job. // `Rem` says this is all right, so we can let `Div` do its job.
self.binop_ignore_overflow(BinOp::Div, &a, &b, dest) self.binop_ignore_overflow(BinOp::Div, &a, &b, dest)
@ -638,9 +650,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max), // `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max),
// but no actual allocation can be big enough for the difference to be noticeable. // but no actual allocation can be big enough for the difference to be noticeable.
let size = size.checked_mul(count, self).ok_or_else(|| { let size = size.checked_mul(count, self).ok_or_else(|| {
err_ub_format!( err_ub_custom!(
"overflow computing total size of `{}`", fluent::const_eval_size_overflow,
if nonoverlapping { "copy_nonoverlapping" } else { "copy" } name = if nonoverlapping { "copy_nonoverlapping" } else { "copy" }
) )
})?; })?;
@ -664,10 +676,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max), // `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max),
// but no actual allocation can be big enough for the difference to be noticeable. // but no actual allocation can be big enough for the difference to be noticeable.
let len = layout let len = layout.size.checked_mul(count, self).ok_or_else(|| {
.size err_ub_custom!(fluent::const_eval_size_overflow, name = "write_bytes")
.checked_mul(count, self) })?;
.ok_or_else(|| err_ub_format!("overflow computing total size of `write_bytes`"))?;
let bytes = std::iter::repeat(byte).take(len.bytes_usize()); let bytes = std::iter::repeat(byte).take(len.bytes_usize());
self.write_bytes_ptr(dst, bytes) self.write_bytes_ptr(dst, bytes)
@ -691,7 +702,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
return Ok(&[]); return Ok(&[]);
}; };
if alloc_ref.has_provenance() { if alloc_ref.has_provenance() {
throw_ub_format!("`raw_eq` on bytes with provenance"); throw_ub_custom!(fluent::const_eval_raw_eq_with_provenance);
} }
alloc_ref.get_bytes_strip_provenance() alloc_ref.get_bytes_strip_provenance()
}; };

View file

@ -19,6 +19,7 @@ use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
use rustc_target::abi::{Align, HasDataLayout, Size}; use rustc_target::abi::{Align, HasDataLayout, Size};
use crate::const_eval::CheckAlignment; use crate::const_eval::CheckAlignment;
use crate::fluent_generated as fluent;
use super::{ use super::{
alloc_range, AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg, alloc_range, AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg,
@ -200,7 +201,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
align: Align, align: Align,
kind: MemoryKind<M::MemoryKind>, kind: MemoryKind<M::MemoryKind>,
) -> InterpResult<'tcx, Pointer<M::Provenance>> { ) -> InterpResult<'tcx, Pointer<M::Provenance>> {
let alloc = Allocation::uninit(size, align, M::PANIC_ON_ALLOC_FAIL)?; let alloc = if M::PANIC_ON_ALLOC_FAIL {
Allocation::uninit(size, align)
} else {
Allocation::try_uninit(size, align)?
};
self.allocate_raw_ptr(alloc, kind) self.allocate_raw_ptr(alloc, kind)
} }
@ -242,9 +247,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
) -> InterpResult<'tcx, Pointer<M::Provenance>> { ) -> InterpResult<'tcx, Pointer<M::Provenance>> {
let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr)?; let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr)?;
if offset.bytes() != 0 { if offset.bytes() != 0 {
throw_ub_format!( throw_ub_custom!(
"reallocating {:?} which does not point to the beginning of an object", fluent::const_eval_realloc_or_alloc_with_offset,
ptr ptr = format!("{ptr:?}"),
kind = "realloc"
); );
} }
@ -280,9 +286,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
trace!("deallocating: {alloc_id:?}"); trace!("deallocating: {alloc_id:?}");
if offset.bytes() != 0 { if offset.bytes() != 0 {
throw_ub_format!( throw_ub_custom!(
"deallocating {:?} which does not point to the beginning of an object", fluent::const_eval_realloc_or_alloc_with_offset,
ptr ptr = format!("{ptr:?}"),
kind = "dealloc",
); );
} }
@ -290,13 +297,25 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Deallocating global memory -- always an error // Deallocating global memory -- always an error
return Err(match self.tcx.try_get_global_alloc(alloc_id) { return Err(match self.tcx.try_get_global_alloc(alloc_id) {
Some(GlobalAlloc::Function(..)) => { Some(GlobalAlloc::Function(..)) => {
err_ub_format!("deallocating {alloc_id:?}, which is a function") err_ub_custom!(
fluent::const_eval_invalid_dealloc,
alloc_id = alloc_id,
kind = "fn",
)
} }
Some(GlobalAlloc::VTable(..)) => { Some(GlobalAlloc::VTable(..)) => {
err_ub_format!("deallocating {alloc_id:?}, which is a vtable") err_ub_custom!(
fluent::const_eval_invalid_dealloc,
alloc_id = alloc_id,
kind = "vtable",
)
} }
Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => { Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => {
err_ub_format!("deallocating {alloc_id:?}, which is static memory") err_ub_custom!(
fluent::const_eval_invalid_dealloc,
alloc_id = alloc_id,
kind = "static_mem"
)
} }
None => err_ub!(PointerUseAfterFree(alloc_id)), None => err_ub!(PointerUseAfterFree(alloc_id)),
} }
@ -304,21 +323,25 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}; };
if alloc.mutability.is_not() { if alloc.mutability.is_not() {
throw_ub_format!("deallocating immutable allocation {alloc_id:?}"); throw_ub_custom!(fluent::const_eval_dealloc_immutable, alloc = alloc_id,);
} }
if alloc_kind != kind { if alloc_kind != kind {
throw_ub_format!( throw_ub_custom!(
"deallocating {alloc_id:?}, which is {alloc_kind} memory, using {kind} deallocation operation" fluent::const_eval_dealloc_kind_mismatch,
alloc = alloc_id,
alloc_kind = format!("{alloc_kind}"),
kind = format!("{kind}"),
); );
} }
if let Some((size, align)) = old_size_and_align { if let Some((size, align)) = old_size_and_align {
if size != alloc.size() || align != alloc.align { if size != alloc.size() || align != alloc.align {
throw_ub_format!( throw_ub_custom!(
"incorrect layout on deallocation: {alloc_id:?} has size {} and alignment {}, but gave size {} and alignment {}", fluent::const_eval_dealloc_incorrect_layout,
alloc.size().bytes(), alloc = alloc_id,
alloc.align.bytes(), size = alloc.size().bytes(),
size.bytes(), align = alloc.align.bytes(),
align.bytes(), size_found = size.bytes(),
align_found = align.bytes(),
) )
} }
} }
@ -1166,7 +1189,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
if (src_offset <= dest_offset && src_offset + size > dest_offset) if (src_offset <= dest_offset && src_offset + size > dest_offset)
|| (dest_offset <= src_offset && dest_offset + size > src_offset) || (dest_offset <= src_offset && dest_offset + size > src_offset)
{ {
throw_ub_format!("copy_nonoverlapping called on overlapping ranges") throw_ub_custom!(fluent::const_eval_copy_nonoverlapping_overlapping);
} }
} }

View file

@ -15,6 +15,7 @@ use super::{
FnVal, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemoryKind, OpTy, Operand, FnVal, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemoryKind, OpTy, Operand,
PlaceTy, Scalar, StackPopCleanup, PlaceTy, Scalar, StackPopCleanup,
}; };
use crate::fluent_generated as fluent;
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
pub(super) fn eval_terminator( pub(super) fn eval_terminator(
@ -172,7 +173,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
InlineAsm { template, ref operands, options, destination, .. } => { InlineAsm { template, ref operands, options, destination, .. } => {
M::eval_inline_asm(self, template, operands, options)?; M::eval_inline_asm(self, template, operands, options)?;
if options.contains(InlineAsmOptions::NORETURN) { if options.contains(InlineAsmOptions::NORETURN) {
throw_ub_format!("returned from noreturn inline assembly"); throw_ub_custom!(fluent::const_eval_noreturn_asm_returned);
} }
self.go_to_block( self.go_to_block(
destination destination
@ -288,15 +289,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
return Ok(()); return Ok(());
} }
// Find next caller arg. // Find next caller arg.
let (caller_arg, caller_abi) = caller_args.next().ok_or_else(|| { let Some((caller_arg, caller_abi)) = caller_args.next() else {
err_ub_format!("calling a function with fewer arguments than it requires") throw_ub_custom!(fluent::const_eval_not_enough_caller_args);
})?; };
// Now, check // Now, check
if !Self::check_argument_compat(caller_abi, callee_abi) { if !Self::check_argument_compat(caller_abi, callee_abi) {
throw_ub_format!( let callee_ty = format!("{}", callee_arg.layout.ty);
"calling a function with argument of type {:?} passing data of type {:?}", let caller_ty = format!("{}", caller_arg.layout.ty);
callee_arg.layout.ty, throw_ub_custom!(
caller_arg.layout.ty fluent::const_eval_incompatible_types,
callee_ty = callee_ty,
caller_ty = caller_ty,
) )
} }
// Special handling for unsized parameters. // Special handling for unsized parameters.
@ -398,10 +401,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
if M::enforce_abi(self) { if M::enforce_abi(self) {
if caller_fn_abi.conv != callee_fn_abi.conv { if caller_fn_abi.conv != callee_fn_abi.conv {
throw_ub_format!( throw_ub_custom!(
"calling a function with calling convention {:?} using calling convention {:?}", fluent::const_eval_incompatible_calling_conventions,
callee_fn_abi.conv, callee_conv = format!("{:?}", callee_fn_abi.conv),
caller_fn_abi.conv caller_conv = format!("{:?}", caller_fn_abi.conv),
) )
} }
} }
@ -508,15 +511,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
"mismatch between callee ABI and callee body arguments" "mismatch between callee ABI and callee body arguments"
); );
if caller_args.next().is_some() { if caller_args.next().is_some() {
throw_ub_format!("calling a function with more arguments than it expected") throw_ub_custom!(fluent::const_eval_too_many_caller_args);
} }
// Don't forget to check the return type! // Don't forget to check the return type!
if !Self::check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret) { if !Self::check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret) {
throw_ub_format!( let callee_ty = format!("{}", callee_fn_abi.ret.layout.ty);
"calling a function with return type {:?} passing \ let caller_ty = format!("{}", caller_fn_abi.ret.layout.ty);
return place of type {:?}", throw_ub_custom!(
callee_fn_abi.ret.layout.ty, fluent::const_eval_incompatible_return_types,
caller_fn_abi.ret.layout.ty, callee_ty = callee_ty,
caller_ty = caller_ty,
) )
} }
}; };
@ -587,9 +591,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let (recv, vptr) = self.unpack_dyn_star(&receiver_place.into())?; let (recv, vptr) = self.unpack_dyn_star(&receiver_place.into())?;
let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?; let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
if dyn_trait != data.principal() { if dyn_trait != data.principal() {
throw_ub_format!( throw_ub_custom!(fluent::const_eval_dyn_star_call_vtable_mismatch);
"`dyn*` call on a pointer whose vtable does not match its type"
);
} }
let recv = recv.assert_mem_place(); // we passed an MPlaceTy to `unpack_dyn_star` so we definitely still have one let recv = recv.assert_mem_place(); // we passed an MPlaceTy to `unpack_dyn_star` so we definitely still have one
@ -609,9 +611,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let vptr = receiver_place.meta.unwrap_meta().to_pointer(self)?; let vptr = receiver_place.meta.unwrap_meta().to_pointer(self)?;
let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?; let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
if dyn_trait != data.principal() { if dyn_trait != data.principal() {
throw_ub_format!( throw_ub_custom!(fluent::const_eval_dyn_call_vtable_mismatch);
"`dyn` call on a pointer whose vtable does not match its type"
);
} }
// It might be surprising that we use a pointer as the receiver even if this // It might be surprising that we use a pointer as the receiver even if this
@ -623,7 +623,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Now determine the actual method to call. We can do that in two different ways and // Now determine the actual method to call. We can do that in two different ways and
// compare them to ensure everything fits. // compare them to ensure everything fits.
let Some(ty::VtblEntry::Method(fn_inst)) = self.get_vtable_entries(vptr)?.get(idx).copied() else { let Some(ty::VtblEntry::Method(fn_inst)) = self.get_vtable_entries(vptr)?.get(idx).copied() else {
throw_ub_format!("`dyn` call trying to call something that is not a method") // FIXME(fee1-dead) these could be variants of the UB info enum instead of this
throw_ub_custom!(fluent::const_eval_dyn_call_not_a_method);
}; };
trace!("Virtual call dispatches to {fn_inst:#?}"); trace!("Virtual call dispatches to {fn_inst:#?}");
if cfg!(debug_assertions) { if cfg!(debug_assertions) {

View file

@ -4,7 +4,7 @@
//! That's useful because it means other passes (e.g. promotion) can rely on `const`s //! That's useful because it means other passes (e.g. promotion) can rely on `const`s
//! to be const-safe. //! to be const-safe.
use std::fmt::{Display, Write}; use std::fmt::Write;
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use either::{Left, Right}; use either::{Left, Right};
@ -12,7 +12,10 @@ use either::{Left, Right};
use rustc_ast::Mutability; use rustc_ast::Mutability;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_middle::mir::interpret::InterpError; use rustc_middle::mir::interpret::{
ExpectedKind, InterpError, InvalidMetaKind, PointerKind, ValidationErrorInfo,
ValidationErrorKind, ValidationErrorKind::*,
};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_span::symbol::{sym, Symbol}; use rustc_span::symbol::{sym, Symbol};
@ -30,14 +33,7 @@ use super::{
}; };
macro_rules! throw_validation_failure { macro_rules! throw_validation_failure {
($where:expr, { $( $what_fmt:tt )* } $( expected { $( $expected_fmt:tt )* } )?) => {{ ($where:expr, $kind: expr) => {{
let mut msg = String::new();
msg.push_str("encountered ");
write!(&mut msg, $($what_fmt)*).unwrap();
$(
msg.push_str(", but expected ");
write!(&mut msg, $($expected_fmt)*).unwrap();
)?
let where_ = &$where; let where_ = &$where;
let path = if !where_.is_empty() { let path = if !where_.is_empty() {
let mut path = String::new(); let mut path = String::new();
@ -46,7 +42,8 @@ macro_rules! throw_validation_failure {
} else { } else {
None None
}; };
throw_ub!(ValidationFailure { path, msg })
throw_ub!(Validation(ValidationErrorInfo { path, kind: $kind }))
}}; }};
} }
@ -82,22 +79,22 @@ macro_rules! throw_validation_failure {
/// ///
macro_rules! try_validation { macro_rules! try_validation {
($e:expr, $where:expr, ($e:expr, $where:expr,
$( $( $p:pat_param )|+ => { $( $what_fmt:tt )* } $( expected { $( $expected_fmt:tt )* } )? ),+ $(,)? $( $( $p:pat_param )|+ => $kind: expr ),+ $(,)?
) => {{ ) => {{
match $e { match $e {
Ok(x) => x, Ok(x) => x,
// We catch the error and turn it into a validation failure. We are okay with // We catch the error and turn it into a validation failure. We are okay with
// allocation here as this can only slow down builds that fail anyway. // allocation here as this can only slow down builds that fail anyway.
Err(e) => match e.kind() { Err(e) => match e.into_parts() {
$( $(
InterpError::UndefinedBehavior($($p)|+) => (InterpError::UndefinedBehavior($($p)|+), _) =>
throw_validation_failure!( throw_validation_failure!(
$where, $where,
{ $( $what_fmt )* } $( expected { $( $expected_fmt )* } )? $kind
) )
),+, ),+,
#[allow(unreachable_patterns)] #[allow(unreachable_patterns)]
_ => Err::<!, _>(e)?, (e, rest) => Err::<!, _>($crate::interpret::InterpErrorInfo::from_parts(e, rest))?,
} }
} }
}}; }};
@ -160,6 +157,7 @@ impl<T: Copy + Eq + Hash + std::fmt::Debug, PATH: Default> RefTracking<T, PATH>
} }
} }
// FIXME make this translatable as well?
/// Format a path /// Format a path
fn write_path(out: &mut String, path: &[PathElem]) { fn write_path(out: &mut String, path: &[PathElem]) {
use self::PathElem::*; use self::PathElem::*;
@ -185,26 +183,6 @@ fn write_path(out: &mut String, path: &[PathElem]) {
} }
} }
// Formats such that a sentence like "expected something {}" to mean
// "expected something <in the given range>" makes sense.
fn wrapping_range_format(r: WrappingRange, max_hi: u128) -> String {
let WrappingRange { start: lo, end: hi } = r;
assert!(hi <= max_hi);
if lo > hi {
format!("less or equal to {}, or greater or equal to {}", hi, lo)
} else if lo == hi {
format!("equal to {}", lo)
} else if lo == 0 {
assert!(hi < max_hi, "should not be printing if the range covers everything");
format!("less or equal to {}", hi)
} else if hi == max_hi {
assert!(lo > 0, "should not be printing if the range covers everything");
format!("greater or equal to {}", lo)
} else {
format!("in the range {:?}", r)
}
}
struct ValidityVisitor<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { struct ValidityVisitor<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> {
/// The `path` may be pushed to, but the part that is present when a function /// The `path` may be pushed to, but the part that is present when a function
/// starts must not be changed! `visit_fields` and `visit_array` rely on /// starts must not be changed! `visit_fields` and `visit_array` rely on
@ -311,19 +289,19 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
fn read_immediate( fn read_immediate(
&self, &self,
op: &OpTy<'tcx, M::Provenance>, op: &OpTy<'tcx, M::Provenance>,
expected: impl Display, expected: ExpectedKind,
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
Ok(try_validation!( Ok(try_validation!(
self.ecx.read_immediate(op), self.ecx.read_immediate(op),
self.path, self.path,
InvalidUninitBytes(None) => { "uninitialized memory" } expected { "{expected}" } InvalidUninitBytes(None) => Uninit { expected }
)) ))
} }
fn read_scalar( fn read_scalar(
&self, &self,
op: &OpTy<'tcx, M::Provenance>, op: &OpTy<'tcx, M::Provenance>,
expected: impl Display, expected: ExpectedKind,
) -> InterpResult<'tcx, Scalar<M::Provenance>> { ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
Ok(self.read_immediate(op, expected)?.to_scalar()) Ok(self.read_immediate(op, expected)?.to_scalar())
} }
@ -342,8 +320,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
self.ecx.get_ptr_vtable(vtable), self.ecx.get_ptr_vtable(vtable),
self.path, self.path,
DanglingIntPointer(..) | DanglingIntPointer(..) |
InvalidVTablePointer(..) => InvalidVTablePointer(..) => InvalidVTablePtr { value: format!("{vtable}") }
{ "{vtable}" } expected { "a vtable pointer" },
); );
// FIXME: check if the type/trait match what ty::Dynamic says? // FIXME: check if the type/trait match what ty::Dynamic says?
} }
@ -366,10 +343,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
fn check_safe_pointer( fn check_safe_pointer(
&mut self, &mut self,
value: &OpTy<'tcx, M::Provenance>, value: &OpTy<'tcx, M::Provenance>,
kind: &str, ptr_kind: PointerKind,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
let place = let place = self.ecx.ref_to_mplace(&self.read_immediate(value, ptr_kind.into())?)?;
self.ecx.ref_to_mplace(&self.read_immediate(value, format_args!("a {kind}"))?)?;
// Handle wide pointers. // Handle wide pointers.
// Check metadata early, for better diagnostics // Check metadata early, for better diagnostics
if place.layout.is_unsized() { if place.layout.is_unsized() {
@ -379,7 +355,10 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
let size_and_align = try_validation!( let size_and_align = try_validation!(
self.ecx.size_and_align_of_mplace(&place), self.ecx.size_and_align_of_mplace(&place),
self.path, self.path,
InvalidMeta(msg) => { "invalid {} metadata: {}", kind, msg }, InvalidMeta(msg) => match msg {
InvalidMetaKind::SliceTooBig => InvalidMetaSliceTooLarge { ptr_kind },
InvalidMetaKind::TooBig => InvalidMetaTooLarge { ptr_kind },
}
); );
let (size, align) = size_and_align let (size, align) = size_and_align
// for the purpose of validity, consider foreign types to have // for the purpose of validity, consider foreign types to have
@ -395,31 +374,30 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message
), ),
self.path, self.path,
AlignmentCheckFailed { required, has } => AlignmentCheckFailed { required, has } => UnalignedPtr {
{ ptr_kind,
"an unaligned {kind} (required {} byte alignment but found {})", required_bytes: required.bytes(),
required.bytes(), found_bytes: has.bytes()
has.bytes(), },
}, DanglingIntPointer(0, _) => NullPtr { ptr_kind },
DanglingIntPointer(0, _) => DanglingIntPointer(i, _) => DanglingPtrNoProvenance {
{ "a null {kind}" }, ptr_kind,
DanglingIntPointer(i, _) => // FIXME this says "null pointer" when null but we need translate
{ pointer: format!("{}", Pointer::<Option<AllocId>>::from_addr_invalid(i))
"a dangling {kind} ({pointer} has no provenance)", },
pointer = Pointer::<Option<AllocId>>::from_addr_invalid(*i), PointerOutOfBounds { .. } => DanglingPtrOutOfBounds {
}, ptr_kind
PointerOutOfBounds { .. } => },
{ "a dangling {kind} (going beyond the bounds of its allocation)" },
// This cannot happen during const-eval (because interning already detects // This cannot happen during const-eval (because interning already detects
// dangling pointers), but it can happen in Miri. // dangling pointers), but it can happen in Miri.
PointerUseAfterFree(..) => PointerUseAfterFree(..) => DanglingPtrUseAfterFree {
{ "a dangling {kind} (use-after-free)" }, ptr_kind,
},
); );
// Do not allow pointers to uninhabited types. // Do not allow pointers to uninhabited types.
if place.layout.abi.is_uninhabited() { if place.layout.abi.is_uninhabited() {
throw_validation_failure!(self.path, let ty = place.layout.ty;
{ "a {kind} pointing to uninhabited type {}", place.layout.ty } throw_validation_failure!(self.path, PtrToUninhabited { ptr_kind, ty })
)
} }
// Recursive checking // Recursive checking
if let Some(ref_tracking) = self.ref_tracking.as_deref_mut() { if let Some(ref_tracking) = self.ref_tracking.as_deref_mut() {
@ -441,9 +419,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
// this check is so important. // this check is so important.
// This check is reachable when the const just referenced the static, // This check is reachable when the const just referenced the static,
// but never read it (so we never entered `before_access_global`). // but never read it (so we never entered `before_access_global`).
throw_validation_failure!(self.path, throw_validation_failure!(self.path, PtrToStatic { ptr_kind });
{ "a {} pointing to a static variable in a constant", kind }
);
} }
// We skip recursively checking other statics. These statics must be sound by // We skip recursively checking other statics. These statics must be sound by
// themselves, and the only way to get broken statics here is by using // themselves, and the only way to get broken statics here is by using
@ -464,9 +440,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
// This should be unreachable, but if someone manages to copy a pointer // This should be unreachable, but if someone manages to copy a pointer
// out of a `static`, then that pointer might point to mutable memory, // out of a `static`, then that pointer might point to mutable memory,
// and we would catch that here. // and we would catch that here.
throw_validation_failure!(self.path, throw_validation_failure!(self.path, PtrToMut { ptr_kind });
{ "a {} pointing to mutable memory in a constant", kind }
);
} }
} }
// Nothing to check for these. // Nothing to check for these.
@ -496,22 +470,24 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
let ty = value.layout.ty; let ty = value.layout.ty;
match ty.kind() { match ty.kind() {
ty::Bool => { ty::Bool => {
let value = self.read_scalar(value, "a boolean")?; let value = self.read_scalar(value, ExpectedKind::Bool)?;
try_validation!( try_validation!(
value.to_bool(), value.to_bool(),
self.path, self.path,
InvalidBool(..) => InvalidBool(..) => ValidationErrorKind::InvalidBool {
{ "{:x}", value } expected { "a boolean" }, value: format!("{value:x}"),
}
); );
Ok(true) Ok(true)
} }
ty::Char => { ty::Char => {
let value = self.read_scalar(value, "a unicode scalar value")?; let value = self.read_scalar(value, ExpectedKind::Char)?;
try_validation!( try_validation!(
value.to_char(), value.to_char(),
self.path, self.path,
InvalidChar(..) => InvalidChar(..) => ValidationErrorKind::InvalidChar {
{ "{:x}", value } expected { "a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`)" }, value: format!("{value:x}"),
}
); );
Ok(true) Ok(true)
} }
@ -521,16 +497,17 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
let value = self.read_scalar( let value = self.read_scalar(
value, value,
if matches!(ty.kind(), ty::Float(..)) { if matches!(ty.kind(), ty::Float(..)) {
"a floating point number" ExpectedKind::Float
} else { } else {
"an integer" ExpectedKind::Int
}, },
)?; )?;
// As a special exception we *do* match on a `Scalar` here, since we truly want // As a special exception we *do* match on a `Scalar` here, since we truly want
// to know its underlying representation (and *not* cast it to an integer). // to know its underlying representation (and *not* cast it to an integer).
if matches!(value, Scalar::Ptr(..)) { if matches!(value, Scalar::Ptr(..)) {
throw_validation_failure!(self.path, throw_validation_failure!(
{ "{:x}", value } expected { "plain (non-pointer) bytes" } self.path,
ExpectedNonPtr { value: format!("{value:x}") }
) )
} }
Ok(true) Ok(true)
@ -540,7 +517,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
// actually enforce the strict rules for raw pointers (mostly because // actually enforce the strict rules for raw pointers (mostly because
// that lets us re-use `ref_to_mplace`). // that lets us re-use `ref_to_mplace`).
let place = let place =
self.ecx.ref_to_mplace(&self.read_immediate(value, "a raw pointer")?)?; self.ecx.ref_to_mplace(&self.read_immediate(value, ExpectedKind::RawPtr)?)?;
if place.layout.is_unsized() { if place.layout.is_unsized() {
self.check_wide_ptr_meta(place.meta, place.layout)?; self.check_wide_ptr_meta(place.meta, place.layout)?;
} }
@ -554,14 +531,14 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
// a ZST). // a ZST).
let layout = self.ecx.layout_of(*ty)?; let layout = self.ecx.layout_of(*ty)?;
if !layout.is_zst() { if !layout.is_zst() {
throw_validation_failure!(self.path, { "mutable reference in a `const`" }); throw_validation_failure!(self.path, MutableRefInConst);
} }
} }
self.check_safe_pointer(value, "reference")?; self.check_safe_pointer(value, PointerKind::Ref)?;
Ok(true) Ok(true)
} }
ty::FnPtr(_sig) => { ty::FnPtr(_sig) => {
let value = self.read_scalar(value, "a function pointer")?; let value = self.read_scalar(value, ExpectedKind::FnPtr)?;
// If we check references recursively, also check that this points to a function. // If we check references recursively, also check that this points to a function.
if let Some(_) = self.ref_tracking { if let Some(_) = self.ref_tracking {
@ -570,19 +547,20 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
self.ecx.get_ptr_fn(ptr), self.ecx.get_ptr_fn(ptr),
self.path, self.path,
DanglingIntPointer(..) | DanglingIntPointer(..) |
InvalidFunctionPointer(..) => InvalidFunctionPointer(..) => InvalidFnPtr {
{ "{ptr}" } expected { "a function pointer" }, value: format!("{ptr}"),
},
); );
// FIXME: Check if the signature matches // FIXME: Check if the signature matches
} else { } else {
// Otherwise (for standalone Miri), we have to still check it to be non-null. // Otherwise (for standalone Miri), we have to still check it to be non-null.
if self.ecx.scalar_may_be_null(value)? { if self.ecx.scalar_may_be_null(value)? {
throw_validation_failure!(self.path, { "a null function pointer" }); throw_validation_failure!(self.path, NullFnPtr);
} }
} }
Ok(true) Ok(true)
} }
ty::Never => throw_validation_failure!(self.path, { "a value of the never type `!`" }), ty::Never => throw_validation_failure!(self.path, NeverVal),
ty::Foreign(..) | ty::FnDef(..) => { ty::Foreign(..) | ty::FnDef(..) => {
// Nothing to check. // Nothing to check.
Ok(true) Ok(true)
@ -629,12 +607,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
if start == 1 && end == max_value { if start == 1 && end == max_value {
// Only null is the niche. So make sure the ptr is NOT null. // Only null is the niche. So make sure the ptr is NOT null.
if self.ecx.scalar_may_be_null(scalar)? { if self.ecx.scalar_may_be_null(scalar)? {
throw_validation_failure!(self.path, throw_validation_failure!(
{ "a potentially null pointer" } self.path,
expected { NullablePtrOutOfRange { range: valid_range, max_value }
"something that cannot possibly fail to be {}",
wrapping_range_format(valid_range, max_value)
}
) )
} else { } else {
return Ok(()); return Ok(());
@ -645,12 +620,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
} else { } else {
// Conservatively, we reject, because the pointer *could* have a bad // Conservatively, we reject, because the pointer *could* have a bad
// value. // value.
throw_validation_failure!(self.path, throw_validation_failure!(
{ "a pointer" } self.path,
expected { PtrOutOfRange { range: valid_range, max_value }
"something that cannot possibly fail to be {}",
wrapping_range_format(valid_range, max_value)
}
) )
} }
} }
@ -659,9 +631,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
if valid_range.contains(bits) { if valid_range.contains(bits) {
Ok(()) Ok(())
} else { } else {
throw_validation_failure!(self.path, throw_validation_failure!(
{ "{}", bits } self.path,
expected { "something {}", wrapping_range_format(valid_range, max_value) } OutOfRange { value: format!("{bits}"), range: valid_range, max_value }
) )
} }
} }
@ -685,10 +657,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
Ok(try_validation!( Ok(try_validation!(
this.ecx.read_discriminant(op), this.ecx.read_discriminant(op),
this.path, this.path,
InvalidTag(val) => InvalidTag(val) => InvalidEnumTag {
{ "{:x}", val } expected { "a valid enum tag" }, value: format!("{val:x}"),
InvalidUninitBytes(None) => },
{ "uninitialized bytes" } expected { "a valid enum tag" },
InvalidUninitBytes(None) => UninitEnumTag,
) )
.1) .1)
}) })
@ -730,7 +703,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
// Special check preventing `UnsafeCell` inside unions in the inner part of constants. // Special check preventing `UnsafeCell` inside unions in the inner part of constants.
if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. })) { if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. })) {
if !op.layout.ty.is_freeze(*self.ecx.tcx, 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`" }); throw_validation_failure!(self.path, UnsafeCell);
} }
} }
Ok(()) Ok(())
@ -738,7 +711,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
#[inline] #[inline]
fn visit_box(&mut self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { fn visit_box(&mut self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
self.check_safe_pointer(op, "box")?; self.check_safe_pointer(op, PointerKind::Box)?;
Ok(()) Ok(())
} }
@ -756,7 +729,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. })) if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. }))
&& def.is_unsafe_cell() && def.is_unsafe_cell()
{ {
throw_validation_failure!(self.path, { "`UnsafeCell` in a `const`" }); throw_validation_failure!(self.path, UnsafeCell);
} }
} }
@ -775,14 +748,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
// MyNewtype and then the scalar in there). // MyNewtype and then the scalar in there).
match op.layout.abi { match op.layout.abi {
Abi::Uninhabited => { Abi::Uninhabited => {
throw_validation_failure!(self.path, let ty = op.layout.ty;
{ "a value of uninhabited type {:?}", op.layout.ty } throw_validation_failure!(self.path, UninhabitedVal { ty });
);
} }
Abi::Scalar(scalar_layout) => { Abi::Scalar(scalar_layout) => {
if !scalar_layout.is_uninit_valid() { if !scalar_layout.is_uninit_valid() {
// There is something to check here. // There is something to check here.
let scalar = self.read_scalar(op, "initialized scalar value")?; let scalar = self.read_scalar(op, ExpectedKind::InitScalar)?;
self.visit_scalar(scalar, scalar_layout)?; self.visit_scalar(scalar, scalar_layout)?;
} }
} }
@ -792,7 +764,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
// the other must be init. // the other must be init.
if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() { if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() {
let (a, b) = let (a, b) =
self.read_immediate(op, "initialized scalar value")?.to_scalar_pair(); self.read_immediate(op, ExpectedKind::InitScalar)?.to_scalar_pair();
self.visit_scalar(a, a_layout)?; self.visit_scalar(a, a_layout)?;
self.visit_scalar(b, b_layout)?; self.visit_scalar(b, b_layout)?;
} }
@ -822,7 +794,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
try_validation!( try_validation!(
self.ecx.read_bytes_ptr_strip_provenance(mplace.ptr, Size::from_bytes(len)), self.ecx.read_bytes_ptr_strip_provenance(mplace.ptr, Size::from_bytes(len)),
self.path, self.path,
InvalidUninitBytes(..) => { "uninitialized data in `str`" }, InvalidUninitBytes(..) => { UninitStr },
); );
} }
ty::Array(tys, ..) | ty::Slice(tys) ty::Array(tys, ..) | ty::Slice(tys)
@ -852,7 +824,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
Left(mplace) => mplace, Left(mplace) => mplace,
Right(imm) => match *imm { Right(imm) => match *imm {
Immediate::Uninit => Immediate::Uninit =>
throw_validation_failure!(self.path, { "uninitialized bytes" }), throw_validation_failure!(self.path, UninitVal),
Immediate::Scalar(..) | Immediate::ScalarPair(..) => Immediate::Scalar(..) | Immediate::ScalarPair(..) =>
bug!("arrays/slices can never have Scalar/ScalarPair layout"), bug!("arrays/slices can never have Scalar/ScalarPair layout"),
} }
@ -888,7 +860,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
.unwrap(); .unwrap();
self.path.push(PathElem::ArrayElem(i)); self.path.push(PathElem::ArrayElem(i));
throw_validation_failure!(self.path, { "uninitialized bytes" }) throw_validation_failure!(self.path, UninitVal)
} }
// Propagate upwards (that will also check for unexpected errors). // Propagate upwards (that will also check for unexpected errors).
@ -929,12 +901,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
match visitor.visit_value(&op) { match visitor.visit_value(&op) {
Ok(()) => Ok(()), Ok(()) => Ok(()),
// Pass through validation failures. // Pass through validation failures.
Err(err) if matches!(err.kind(), err_ub!(ValidationFailure { .. })) => Err(err), Err(err) if matches!(err.kind(), err_ub!(Validation { .. })) => Err(err),
// Complain about any other kind of UB error -- those are bad because we'd like to // Complain about any other kind of UB error -- those are bad because we'd like to
// report them in a way that shows *where* in the value the issue lies. // report them in a way that shows *where* in the value the issue lies.
Err(err) if matches!(err.kind(), InterpError::UndefinedBehavior(_)) => { Err(err) if matches!(err.kind(), InterpError::UndefinedBehavior(_)) => {
err.print_backtrace(); let (err, backtrace) = err.into_parts();
bug!("Unexpected Undefined Behavior error during validation: {}", err); backtrace.print_backtrace();
bug!("Unexpected Undefined Behavior error during validation: {err:?}");
} }
// Pass through everything else. // Pass through everything else.
Err(err) => Err(err), Err(err) => Err(err),

View file

@ -4,6 +4,7 @@ Rust MIR: a lowered representation of Rust.
*/ */
#![deny(rustc::untranslatable_diagnostic)]
#![feature(assert_matches)] #![feature(assert_matches)]
#![feature(box_patterns)] #![feature(box_patterns)]
#![feature(decl_macro)] #![feature(decl_macro)]
@ -33,6 +34,8 @@ pub mod interpret;
pub mod transform; pub mod transform;
pub mod util; pub mod util;
pub use errors::ReportErrorExt;
use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
use rustc_fluent_macro::fluent_messages; use rustc_fluent_macro::fluent_messages;
use rustc_middle::query::Providers; use rustc_middle::query::Providers;

View file

@ -2,9 +2,7 @@
use hir::def_id::LocalDefId; use hir::def_id::LocalDefId;
use hir::{ConstContext, LangItem}; use hir::{ConstContext, LangItem};
use rustc_errors::{ use rustc_errors::{error_code, DiagnosticBuilder, ErrorGuaranteed};
error_code, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed,
};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;
@ -152,7 +150,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
if let Ok(Some(ImplSource::UserDefined(data))) = implsrc { if let Ok(Some(ImplSource::UserDefined(data))) = implsrc {
let span = tcx.def_span(data.impl_def_id); let span = tcx.def_span(data.impl_def_id);
err.span_note(span, "impl defined here, but it is not `const`"); err.subdiagnostic(errors::NonConstImplNote { span });
} }
} }
_ => {} _ => {}
@ -166,26 +164,30 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
let mut err = match call_kind { let mut err = match call_kind {
CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => { CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => {
macro_rules! error { macro_rules! error {
($fmt:literal) => { ($err:ident) => {
struct_span_err!(tcx.sess, span, E0015, $fmt, self_ty, ccx.const_kind()) tcx.sess.create_err(errors::$err {
span,
ty: self_ty,
kind: ccx.const_kind(),
})
}; };
} }
let mut err = match kind { let mut err = match kind {
CallDesugaringKind::ForLoopIntoIter => { CallDesugaringKind::ForLoopIntoIter => {
error!("cannot convert `{}` into an iterator in {}s") error!(NonConstForLoopIntoIter)
} }
CallDesugaringKind::QuestionBranch => { CallDesugaringKind::QuestionBranch => {
error!("`?` cannot determine the branch of `{}` in {}s") error!(NonConstQuestionBranch)
} }
CallDesugaringKind::QuestionFromResidual => { CallDesugaringKind::QuestionFromResidual => {
error!("`?` cannot convert from residual of `{}` in {}s") error!(NonConstQuestionFromResidual)
} }
CallDesugaringKind::TryBlockFromOutput => { CallDesugaringKind::TryBlockFromOutput => {
error!("`try` block cannot convert `{}` to the result in {}s") error!(NonConstTryBlockFromOutput)
} }
CallDesugaringKind::Await => { CallDesugaringKind::Await => {
error!("cannot convert `{}` into a future in {}s") error!(NonConstAwait)
} }
}; };
@ -193,49 +195,31 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
err err
} }
CallKind::FnCall { fn_trait_id, self_ty } => { CallKind::FnCall { fn_trait_id, self_ty } => {
let mut err = struct_span_err!( let note = match self_ty.kind() {
tcx.sess,
span,
E0015,
"cannot call non-const closure in {}s",
ccx.const_kind(),
);
match self_ty.kind() {
FnDef(def_id, ..) => { FnDef(def_id, ..) => {
let span = tcx.def_span(*def_id); let span = tcx.def_span(*def_id);
if ccx.tcx.is_const_fn_raw(*def_id) { if ccx.tcx.is_const_fn_raw(*def_id) {
span_bug!(span, "calling const FnDef errored when it shouldn't"); span_bug!(span, "calling const FnDef errored when it shouldn't");
} }
err.span_note(span, "function defined here, but it is not `const`"); Some(errors::NonConstClosureNote::FnDef { span })
} }
FnPtr(..) => { FnPtr(..) => Some(errors::NonConstClosureNote::FnPtr),
err.note(format!( Closure(..) => Some(errors::NonConstClosureNote::Closure),
"function pointers need an RFC before allowed to be called in {}s", _ => None,
ccx.const_kind() };
));
} let mut err = tcx.sess.create_err(errors::NonConstClosure {
Closure(..) => { span,
err.note(format!( kind: ccx.const_kind(),
"closures need an RFC before allowed to be called in {}s", note,
ccx.const_kind() });
));
}
_ => {}
}
diag_trait(&mut err, self_ty, fn_trait_id); diag_trait(&mut err, self_ty, fn_trait_id);
err err
} }
CallKind::Operator { trait_id, self_ty, .. } => { CallKind::Operator { trait_id, self_ty, .. } => {
let mut err = struct_span_err!( let mut sugg = None;
tcx.sess,
span,
E0015,
"cannot call non-const operator in {}s",
ccx.const_kind()
);
if Some(trait_id) == ccx.tcx.lang_items().eq_trait() { if Some(trait_id) == ccx.tcx.lang_items().eq_trait() {
match (substs[0].unpack(), substs[1].unpack()) { match (substs[0].unpack(), substs[1].unpack()) {
@ -260,14 +244,11 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
let rhs_pos = let rhs_pos =
span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx); span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx);
let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos); let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
err.multipart_suggestion( sugg = Some(errors::ConsiderDereferencing {
"consider dereferencing here", deref,
vec![ span: span.shrink_to_lo(),
(span.shrink_to_lo(), deref.clone()), rhs_span,
(rhs_span, deref), });
],
Applicability::MachineApplicable,
);
} }
} }
} }
@ -275,26 +256,29 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
_ => {} _ => {}
} }
} }
let mut err = tcx.sess.create_err(errors::NonConstOperator {
span,
kind: ccx.const_kind(),
sugg,
});
diag_trait(&mut err, self_ty, trait_id); diag_trait(&mut err, self_ty, trait_id);
err err
} }
CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => { CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => {
let mut err = struct_span_err!(
tcx.sess,
span,
E0015,
"cannot perform deref coercion on `{}` in {}s",
self_ty,
ccx.const_kind()
);
err.note(format!("attempting to deref into `{}`", deref_target_ty));
// Check first whether the source is accessible (issue #87060) // Check first whether the source is accessible (issue #87060)
if tcx.sess.source_map().is_span_accessible(deref_target) { let target = if tcx.sess.source_map().is_span_accessible(deref_target) {
err.span_note(deref_target, "deref defined here"); Some(deref_target)
} } else {
None
};
let mut err = tcx.sess.create_err(errors::NonConstDerefCoercion {
span,
ty: self_ty,
kind: ccx.const_kind(),
target_ty: deref_target_ty,
deref_target: target,
});
diag_trait(&mut err, self_ty, tcx.require_lang_item(LangItem::Deref, Some(span))); diag_trait(&mut err, self_ty, tcx.require_lang_item(LangItem::Deref, Some(span)));
err err
@ -432,21 +416,12 @@ impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
ccx: &ConstCx<'_, 'tcx>, ccx: &ConstCx<'_, 'tcx>,
span: Span, span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let mut err = struct_span_err!( ccx.tcx.sess.create_err(errors::LiveDrop {
ccx.tcx.sess,
span, span,
E0493, dropped_ty: self.dropped_ty,
"destructor of `{}` cannot be evaluated at compile-time", kind: ccx.const_kind(),
self.dropped_ty, dropped_at: self.dropped_at,
); })
err.span_label(
span,
format!("the destructor for this type cannot be evaluated in {}s", ccx.const_kind()),
);
if let Some(span) = self.dropped_at {
err.span_label(span, "value is dropped here");
}
err
} }
} }

View file

@ -492,6 +492,10 @@ impl MultiSpan {
replacements_occurred replacements_occurred
} }
pub fn pop_span_label(&mut self) -> Option<(Span, DiagnosticMessage)> {
self.span_labels.pop()
}
/// Returns the strings to highlight. We always ensure that there /// Returns the strings to highlight. We always ensure that there
/// is an entry for each of the primary spans -- for each primary /// is an entry for each of the primary spans -- for each primary
/// span `P`, if there is at least one label with span `P`, we return /// span `P`, if there is at least one label with span `P`, we return

View file

@ -8,7 +8,11 @@ errors_target_invalid_address_space =
invalid address space `{$addr_space}` for `{$cause}` in "data-layout": {$err} invalid address space `{$addr_space}` for `{$cause}` in "data-layout": {$err}
errors_target_invalid_alignment = errors_target_invalid_alignment =
invalid alignment for `{$cause}` in "data-layout": {$err} invalid alignment for `{$cause}` in "data-layout": `{$align}` is {$err_kind ->
[not_power_of_two] not a power of 2
[too_large] too large
*[other] {""}
}
errors_target_invalid_bits = errors_target_invalid_bits =
invalid {$kind} `{$bit}` for `{$cause}` in "data-layout": {$err} invalid {$kind} `{$bit}` for `{$cause}` in "data-layout": {$err}

View file

@ -10,7 +10,7 @@ use rustc_lint_defs::{Applicability, LintExpectationId};
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
use rustc_span::{Span, DUMMY_SP}; use rustc_span::{Span, DUMMY_SP};
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt::{self, Debug};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::panic::Location; use std::panic::Location;
@ -33,7 +33,7 @@ pub type DiagnosticArgName<'source> = Cow<'source, str>;
#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
pub enum DiagnosticArgValue<'source> { pub enum DiagnosticArgValue<'source> {
Str(Cow<'source, str>), Str(Cow<'source, str>),
Number(usize), Number(i128),
StrListSepByAnd(Vec<Cow<'source, str>>), StrListSepByAnd(Vec<Cow<'source, str>>),
} }

View file

@ -60,10 +60,8 @@ into_diagnostic_arg_using_display!(
u8, u8,
i16, i16,
u16, u16,
i32,
u32, u32,
i64, i64,
u64,
i128, i128,
u128, u128,
std::io::Error, std::io::Error,
@ -80,6 +78,18 @@ into_diagnostic_arg_using_display!(
ExitStatus, ExitStatus,
); );
impl IntoDiagnosticArg for i32 {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Number(self.into())
}
}
impl IntoDiagnosticArg for u64 {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Number(self.into())
}
}
impl IntoDiagnosticArg for bool { impl IntoDiagnosticArg for bool {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
if self { if self {
@ -134,7 +144,7 @@ impl IntoDiagnosticArg for PathBuf {
impl IntoDiagnosticArg for usize { impl IntoDiagnosticArg for usize {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Number(self) DiagnosticArgValue::Number(self as i128)
} }
} }
@ -147,9 +157,9 @@ impl IntoDiagnosticArg for PanicStrategy {
impl IntoDiagnosticArg for hir::ConstContext { impl IntoDiagnosticArg for hir::ConstContext {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Borrowed(match self { DiagnosticArgValue::Str(Cow::Borrowed(match self {
hir::ConstContext::ConstFn => "constant function", hir::ConstContext::ConstFn => "const_fn",
hir::ConstContext::Static(_) => "static", hir::ConstContext::Static(_) => "static",
hir::ConstContext::Const => "constant", hir::ConstContext::Const => "const",
})) }))
} }
} }
@ -254,7 +264,8 @@ impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> {
TargetDataLayoutErrors::InvalidAlignment { cause, err } => { TargetDataLayoutErrors::InvalidAlignment { cause, err } => {
diag = handler.struct_fatal(fluent::errors_target_invalid_alignment); diag = handler.struct_fatal(fluent::errors_target_invalid_alignment);
diag.set_arg("cause", cause); diag.set_arg("cause", cause);
diag.set_arg("err", err); diag.set_arg("err_kind", err.diag_ident());
diag.set_arg("align", err.align());
diag diag
} }
TargetDataLayoutErrors::InconsistentTargetArchitecture { dl, target } => { TargetDataLayoutErrors::InconsistentTargetArchitecture { dl, target } => {

View file

@ -177,7 +177,7 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) {
} }
// Generic statics are rejected, but we still reach this case. // Generic statics are rejected, but we still reach this case.
Err(e) => { Err(e) => {
tcx.sess.delay_span_bug(span, e.to_string()); tcx.sess.delay_span_bug(span, format!("{e:?}"));
return; return;
} }
}; };

View file

@ -14,6 +14,8 @@ use syn::Token;
use syn::{parse_quote, spanned::Spanned, Attribute, Meta, Path, Type}; use syn::{parse_quote, spanned::Spanned, Attribute, Meta, Path, Type};
use synstructure::{BindingInfo, Structure, VariantInfo}; use synstructure::{BindingInfo, Structure, VariantInfo};
use super::utils::SubdiagnosticVariant;
/// What kind of diagnostic is being derived - a fatal/error/warning or a lint? /// What kind of diagnostic is being derived - a fatal/error/warning or a lint?
#[derive(Clone, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq)]
pub(crate) enum DiagnosticDeriveKind { pub(crate) enum DiagnosticDeriveKind {
@ -150,19 +152,19 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
fn parse_subdiag_attribute( fn parse_subdiag_attribute(
&self, &self,
attr: &Attribute, attr: &Attribute,
) -> Result<Option<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> { ) -> Result<Option<(SubdiagnosticKind, Path, bool)>, DiagnosticDeriveError> {
let Some((subdiag, slug)) = SubdiagnosticKind::from_attr(attr, self)? else { let Some(subdiag) = SubdiagnosticVariant::from_attr(attr, self)? else {
// Some attributes aren't errors - like documentation comments - but also aren't // Some attributes aren't errors - like documentation comments - but also aren't
// subdiagnostics. // subdiagnostics.
return Ok(None); return Ok(None);
}; };
if let SubdiagnosticKind::MultipartSuggestion { .. } = subdiag { if let SubdiagnosticKind::MultipartSuggestion { .. } = subdiag.kind {
throw_invalid_attr!(attr, |diag| diag throw_invalid_attr!(attr, |diag| diag
.help("consider creating a `Subdiagnostic` instead")); .help("consider creating a `Subdiagnostic` instead"));
} }
let slug = slug.unwrap_or_else(|| match subdiag { let slug = subdiag.slug.unwrap_or_else(|| match subdiag.kind {
SubdiagnosticKind::Label => parse_quote! { _subdiag::label }, SubdiagnosticKind::Label => parse_quote! { _subdiag::label },
SubdiagnosticKind::Note => parse_quote! { _subdiag::note }, SubdiagnosticKind::Note => parse_quote! { _subdiag::note },
SubdiagnosticKind::Help => parse_quote! { _subdiag::help }, SubdiagnosticKind::Help => parse_quote! { _subdiag::help },
@ -171,7 +173,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(), SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(),
}); });
Ok(Some((subdiag, slug))) Ok(Some((subdiag.kind, slug, subdiag.no_span)))
} }
/// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct /// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct
@ -229,7 +231,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
return Ok(tokens); return Ok(tokens);
} }
let Some((subdiag, slug)) = self.parse_subdiag_attribute(attr)? else { let Some((subdiag, slug, _no_span)) = self.parse_subdiag_attribute(attr)? else {
// Some attributes aren't errors - like documentation comments - but also aren't // Some attributes aren't errors - like documentation comments - but also aren't
// subdiagnostics. // subdiagnostics.
return Ok(quote! {}); return Ok(quote! {});
@ -380,7 +382,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
_ => (), _ => (),
} }
let Some((subdiag, slug)) = self.parse_subdiag_attribute(attr)? else { let Some((subdiag, slug, _no_span)) = self.parse_subdiag_attribute(attr)? else {
// Some attributes aren't errors - like documentation comments - but also aren't // Some attributes aren't errors - like documentation comments - but also aren't
// subdiagnostics. // subdiagnostics.
return Ok(quote! {}); return Ok(quote! {});

View file

@ -14,6 +14,8 @@ use quote::{format_ident, quote};
use syn::{spanned::Spanned, Attribute, Meta, MetaList, Path}; use syn::{spanned::Spanned, Attribute, Meta, MetaList, Path};
use synstructure::{BindingInfo, Structure, VariantInfo}; use synstructure::{BindingInfo, Structure, VariantInfo};
use super::utils::SubdiagnosticVariant;
/// The central struct for constructing the `add_to_diagnostic` method from an annotated struct. /// The central struct for constructing the `add_to_diagnostic` method from an annotated struct.
pub(crate) struct SubdiagnosticDeriveBuilder { pub(crate) struct SubdiagnosticDeriveBuilder {
diag: syn::Ident, diag: syn::Ident,
@ -180,11 +182,13 @@ impl<'a> FromIterator<&'a SubdiagnosticKind> for KindsStatistics {
} }
impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
fn identify_kind(&mut self) -> Result<Vec<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> { fn identify_kind(
&mut self,
) -> Result<Vec<(SubdiagnosticKind, Path, bool)>, DiagnosticDeriveError> {
let mut kind_slugs = vec![]; let mut kind_slugs = vec![];
for attr in self.variant.ast().attrs { for attr in self.variant.ast().attrs {
let Some((kind, slug)) = SubdiagnosticKind::from_attr(attr, self)? else { let Some(SubdiagnosticVariant { kind, slug, no_span }) = SubdiagnosticVariant::from_attr(attr, self)? else {
// Some attributes aren't errors - like documentation comments - but also aren't // Some attributes aren't errors - like documentation comments - but also aren't
// subdiagnostics. // subdiagnostics.
continue; continue;
@ -202,7 +206,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
); );
}; };
kind_slugs.push((kind, slug)); kind_slugs.push((kind, slug, no_span));
} }
Ok(kind_slugs) Ok(kind_slugs)
@ -487,7 +491,8 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
} }
}; };
let kind_stats: KindsStatistics = kind_slugs.iter().map(|(kind, _slug)| kind).collect(); let kind_stats: KindsStatistics =
kind_slugs.iter().map(|(kind, _slug, _no_span)| kind).collect();
let init = if kind_stats.has_multipart_suggestion { let init = if kind_stats.has_multipart_suggestion {
quote! { let mut suggestions = Vec::new(); } quote! { let mut suggestions = Vec::new(); }
@ -508,13 +513,17 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
let diag = &self.parent.diag; let diag = &self.parent.diag;
let f = &self.parent.f; let f = &self.parent.f;
let mut calls = TokenStream::new(); let mut calls = TokenStream::new();
for (kind, slug) in kind_slugs { for (kind, slug, no_span) in kind_slugs {
let message = format_ident!("__message"); let message = format_ident!("__message");
calls.extend( calls.extend(
quote! { let #message = #f(#diag, crate::fluent_generated::#slug.into()); }, quote! { let #message = #f(#diag, crate::fluent_generated::#slug.into()); },
); );
let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); let name = format_ident!(
"{}{}",
if span_field.is_some() && !no_span { "span_" } else { "" },
kind
);
let call = match kind { let call = match kind {
SubdiagnosticKind::Suggestion { SubdiagnosticKind::Suggestion {
suggestion_kind, suggestion_kind,
@ -566,7 +575,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
} }
} }
_ => { _ => {
if let Some(span) = span_field { if let Some(span) = span_field && !no_span {
quote! { #diag.#name(#span, #message); } quote! { #diag.#name(#span, #message); }
} else { } else {
quote! { #diag.#name(#message); } quote! { #diag.#name(#message); }

View file

@ -597,14 +597,20 @@ pub(super) enum SubdiagnosticKind {
}, },
} }
impl SubdiagnosticKind { pub(super) struct SubdiagnosticVariant {
/// Constructs a `SubdiagnosticKind` from a field or type attribute such as `#[note]`, pub(super) kind: SubdiagnosticKind,
/// `#[error(parser::add_paren)]` or `#[suggestion(code = "...")]`. Returns the pub(super) slug: Option<Path>,
pub(super) no_span: bool,
}
impl SubdiagnosticVariant {
/// Constructs a `SubdiagnosticVariant` from a field or type attribute such as `#[note]`,
/// `#[error(parser::add_paren, no_span)]` or `#[suggestion(code = "...")]`. Returns the
/// `SubdiagnosticKind` and the diagnostic slug, if specified. /// `SubdiagnosticKind` and the diagnostic slug, if specified.
pub(super) fn from_attr( pub(super) fn from_attr(
attr: &Attribute, attr: &Attribute,
fields: &impl HasFieldMap, fields: &impl HasFieldMap,
) -> Result<Option<(SubdiagnosticKind, Option<Path>)>, DiagnosticDeriveError> { ) -> Result<Option<SubdiagnosticVariant>, DiagnosticDeriveError> {
// Always allow documentation comments. // Always allow documentation comments.
if is_doc_comment(attr) { if is_doc_comment(attr) {
return Ok(None); return Ok(None);
@ -679,7 +685,7 @@ impl SubdiagnosticKind {
| SubdiagnosticKind::Help | SubdiagnosticKind::Help
| SubdiagnosticKind::Warn | SubdiagnosticKind::Warn
| SubdiagnosticKind::MultipartSuggestion { .. } => { | SubdiagnosticKind::MultipartSuggestion { .. } => {
return Ok(Some((kind, None))); return Ok(Some(SubdiagnosticVariant { kind, slug: None, no_span: false }));
} }
SubdiagnosticKind::Suggestion { .. } => { SubdiagnosticKind::Suggestion { .. } => {
throw_span_err!(span, "suggestion without `code = \"...\"`") throw_span_err!(span, "suggestion without `code = \"...\"`")
@ -696,11 +702,14 @@ impl SubdiagnosticKind {
let mut first = true; let mut first = true;
let mut slug = None; let mut slug = None;
let mut no_span = false;
list.parse_nested_meta(|nested| { list.parse_nested_meta(|nested| {
if nested.input.is_empty() || nested.input.peek(Token![,]) { if nested.input.is_empty() || nested.input.peek(Token![,]) {
if first { if first {
slug = Some(nested.path); slug = Some(nested.path);
} else if nested.path.is_ident("no_span") {
no_span = true;
} else { } else {
span_err(nested.input.span().unwrap(), "a diagnostic slug must be the first argument to the attribute").emit(); span_err(nested.input.span().unwrap(), "a diagnostic slug must be the first argument to the attribute").emit();
} }
@ -775,19 +784,19 @@ impl SubdiagnosticKind {
(_, SubdiagnosticKind::Suggestion { .. }) => { (_, SubdiagnosticKind::Suggestion { .. }) => {
span_err(path_span, "invalid nested attribute") span_err(path_span, "invalid nested attribute")
.help( .help(
"only `style`, `code` and `applicability` are valid nested attributes", "only `no_span`, `style`, `code` and `applicability` are valid nested attributes",
) )
.emit(); .emit();
has_errors = true; has_errors = true;
} }
(_, SubdiagnosticKind::MultipartSuggestion { .. }) => { (_, SubdiagnosticKind::MultipartSuggestion { .. }) => {
span_err(path_span, "invalid nested attribute") span_err(path_span, "invalid nested attribute")
.help("only `style` and `applicability` are valid nested attributes") .help("only `no_span`, `style` and `applicability` are valid nested attributes")
.emit(); .emit();
has_errors = true; has_errors = true;
} }
_ => { _ => {
span_err(path_span, "invalid nested attribute").emit(); span_err(path_span, "only `no_span` is a valid nested attribute").emit();
has_errors = true; has_errors = true;
} }
} }
@ -831,7 +840,7 @@ impl SubdiagnosticKind {
| SubdiagnosticKind::Warn => {} | SubdiagnosticKind::Warn => {}
} }
Ok(Some((kind, slug))) Ok(Some(SubdiagnosticVariant { kind, slug, no_span }))
} }
} }

View file

@ -1,3 +1,38 @@
middle_adjust_for_foreign_abi_error =
target architecture {$arch} does not support `extern {$abi}` ABI
middle_assert_async_resume_after_panic = `async fn` resumed after panicking
middle_assert_async_resume_after_return = `async fn` resumed after completion
middle_assert_divide_by_zero =
attempt to divide `{$val}` by zero
middle_assert_generator_resume_after_panic = generator resumed after panicking
middle_assert_generator_resume_after_return = generator resumed after completion
middle_assert_misaligned_ptr_deref =
misaligned pointer dereference: address must be a multiple of {$required} but is {$found}
middle_assert_op_overflow =
attempt to compute `{$left} {$op} {$right}`, which would overflow
middle_assert_overflow_neg =
attempt to negate `{$val}`, which would overflow
middle_assert_remainder_by_zero =
attempt to calculate the remainder of `{$val}` with a divisor of zero
middle_assert_shl_overflow =
attempt to shift left by `{$val}`, which would overflow
middle_assert_shr_overflow =
attempt to shift right by `{$val}`, which would overflow
middle_bounds_check =
index out of bounds: the length is {$len} but the index is {$index}
middle_cannot_be_normalized = middle_cannot_be_normalized =
unable to determine layout for `{$ty}` because `{$failure_ty}` cannot be normalized unable to determine layout for `{$ty}` because `{$failure_ty}` cannot be normalized

View file

@ -1,3 +1,7 @@
use std::borrow::Cow;
use std::fmt;
use rustc_errors::{DiagnosticArgValue, DiagnosticMessage};
use rustc_macros::Diagnostic; use rustc_macros::Diagnostic;
use rustc_span::{Span, Symbol}; use rustc_span::{Span, Symbol};
@ -88,3 +92,54 @@ pub(super) struct ConstNotUsedTraitAlias {
#[primary_span] #[primary_span]
pub span: Span, pub span: Span,
} }
pub struct CustomSubdiagnostic<'a> {
pub msg: fn() -> DiagnosticMessage,
pub add_args:
Box<dyn FnOnce(&mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>)) + 'a>,
}
impl<'a> CustomSubdiagnostic<'a> {
pub fn label(x: fn() -> DiagnosticMessage) -> Self {
Self::label_and_then(x, |_| {})
}
pub fn label_and_then<
F: FnOnce(&mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>)) + 'a,
>(
msg: fn() -> DiagnosticMessage,
f: F,
) -> Self {
Self { msg, add_args: Box::new(move |x| f(x)) }
}
}
impl fmt::Debug for CustomSubdiagnostic<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CustomSubdiagnostic").finish_non_exhaustive()
}
}
#[derive(Diagnostic)]
pub enum LayoutError<'tcx> {
#[diag(middle_unknown_layout)]
Unknown { ty: Ty<'tcx> },
#[diag(middle_values_too_big)]
Overflow { ty: Ty<'tcx> },
#[diag(middle_cannot_be_normalized)]
NormalizationFailure { ty: Ty<'tcx>, failure_ty: String },
#[diag(middle_cycle)]
Cycle,
}
#[derive(Diagnostic)]
#[diag(middle_adjust_for_foreign_abi_error)]
pub struct UnsupportedFnAbi {
pub arch: Symbol,
pub abi: &'static str,
}
/// Used by `rustc_const_eval`
pub use crate::fluent_generated::middle_adjust_for_foreign_abi_error;

View file

@ -48,6 +48,7 @@
#![feature(associated_type_bounds)] #![feature(associated_type_bounds)]
#![feature(rustc_attrs)] #![feature(rustc_attrs)]
#![feature(control_flow_enum)] #![feature(control_flow_enum)]
#![feature(trait_upcasting)]
#![feature(trusted_step)] #![feature(trusted_step)]
#![feature(try_blocks)] #![feature(try_blocks)]
#![feature(try_reserve_kind)] #![feature(try_reserve_kind)]
@ -86,7 +87,7 @@ mod macros;
#[macro_use] #[macro_use]
pub mod arena; pub mod arena;
pub(crate) mod error; pub mod error;
pub mod hir; pub mod hir;
pub mod infer; pub mod infer;
pub mod lint; pub mod lint;

View file

@ -296,25 +296,13 @@ impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> {
Allocation::from_bytes(slice, Align::ONE, Mutability::Not) Allocation::from_bytes(slice, Align::ONE, Mutability::Not)
} }
/// Try to create an Allocation of `size` bytes, failing if there is not enough memory fn uninit_inner<R>(size: Size, align: Align, fail: impl FnOnce() -> R) -> Result<Self, R> {
/// available to the compiler to do so. // This results in an error that can happen non-deterministically, since the memory
/// // available to the compiler can change between runs. Normally queries are always
/// If `panic_on_fail` is true, this will never return `Err`. // deterministic. However, we can be non-deterministic here because all uses of const
pub fn uninit<'tcx>(size: Size, align: Align, panic_on_fail: bool) -> InterpResult<'tcx, Self> { // evaluation (including ConstProp!) will make compilation fail (via hard error
let bytes = Bytes::zeroed(size, align).ok_or_else(|| { // or ICE) upon encountering a `MemoryExhausted` error.
// This results in an error that can happen non-deterministically, since the memory let bytes = Bytes::zeroed(size, align).ok_or_else(fail)?;
// available to the compiler can change between runs. Normally queries are always
// deterministic. However, we can be non-deterministic here because all uses of const
// evaluation (including ConstProp!) will make compilation fail (via hard error
// or ICE) upon encountering a `MemoryExhausted` error.
if panic_on_fail {
panic!("Allocation::uninit called with panic_on_fail had allocation failure")
}
ty::tls::with(|tcx| {
tcx.sess.delay_span_bug(DUMMY_SP, "exhausted memory during interpretation")
});
InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted)
})?;
Ok(Allocation { Ok(Allocation {
bytes, bytes,
@ -325,6 +313,28 @@ impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> {
extra: (), extra: (),
}) })
} }
/// Try to create an Allocation of `size` bytes, failing if there is not enough memory
/// available to the compiler to do so.
pub fn try_uninit<'tcx>(size: Size, align: Align) -> InterpResult<'tcx, Self> {
Self::uninit_inner(size, align, || {
ty::tls::with(|tcx| {
tcx.sess.delay_span_bug(DUMMY_SP, "exhausted memory during interpretation")
});
InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted).into()
})
}
/// Try to create an Allocation of `size` bytes, panics if there is not enough memory
/// available to the compiler to do so.
pub fn uninit(size: Size, align: Align) -> Self {
match Self::uninit_inner(size, align, || {
panic!("Allocation::uninit called with panic_on_fail had allocation failure");
}) {
Ok(x) => x,
Err(x) => x,
}
}
} }
impl<Bytes: AllocBytes> Allocation<AllocId, (), Bytes> { impl<Bytes: AllocBytes> Allocation<AllocId, (), Bytes> {

View file

@ -5,11 +5,15 @@ use crate::query::TyCtxtAt;
use crate::ty::{layout, tls, Ty, ValTree}; use crate::ty::{layout, tls, Ty, ValTree};
use rustc_data_structures::sync::Lock; use rustc_data_structures::sync::Lock;
use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorGuaranteed}; use rustc_errors::{
struct_span_err, DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed,
IntoDiagnosticArg,
};
use rustc_macros::HashStable; use rustc_macros::HashStable;
use rustc_session::CtfeBacktrace; use rustc_session::CtfeBacktrace;
use rustc_span::def_id::DefId; use rustc_span::def_id::DefId;
use rustc_target::abi::{call, Align, Size}; use rustc_target::abi::{call, Align, Size, WrappingRange};
use std::borrow::Cow;
use std::{any::Any, backtrace::Backtrace, fmt}; use std::{any::Any, backtrace::Backtrace, fmt};
#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
@ -91,20 +95,53 @@ pub struct InterpErrorInfo<'tcx>(Box<InterpErrorInfoInner<'tcx>>);
#[derive(Debug)] #[derive(Debug)]
struct InterpErrorInfoInner<'tcx> { struct InterpErrorInfoInner<'tcx> {
kind: InterpError<'tcx>, kind: InterpError<'tcx>,
backtrace: InterpErrorBacktrace,
}
#[derive(Debug)]
pub struct InterpErrorBacktrace {
backtrace: Option<Box<Backtrace>>, backtrace: Option<Box<Backtrace>>,
} }
impl fmt::Display for InterpErrorInfo<'_> { impl InterpErrorBacktrace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { pub fn new() -> InterpErrorBacktrace {
write!(f, "{}", self.0.kind) let capture_backtrace = tls::with_opt(|tcx| {
if let Some(tcx) = tcx {
*Lock::borrow(&tcx.sess.ctfe_backtrace)
} else {
CtfeBacktrace::Disabled
}
});
let backtrace = match capture_backtrace {
CtfeBacktrace::Disabled => None,
CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())),
CtfeBacktrace::Immediate => {
// Print it now.
let backtrace = Backtrace::force_capture();
print_backtrace(&backtrace);
None
}
};
InterpErrorBacktrace { backtrace }
}
pub fn print_backtrace(&self) {
if let Some(backtrace) = self.backtrace.as_ref() {
print_backtrace(backtrace);
}
} }
} }
impl<'tcx> InterpErrorInfo<'tcx> { impl<'tcx> InterpErrorInfo<'tcx> {
pub fn print_backtrace(&self) { pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self {
if let Some(backtrace) = self.0.backtrace.as_ref() { Self(Box::new(InterpErrorInfoInner { kind, backtrace }))
print_backtrace(backtrace); }
}
pub fn into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace) {
let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace }) = self;
(kind, backtrace)
} }
pub fn into_kind(self) -> InterpError<'tcx> { pub fn into_kind(self) -> InterpError<'tcx> {
@ -130,32 +167,17 @@ impl From<ErrorGuaranteed> for InterpErrorInfo<'_> {
impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> { impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
fn from(kind: InterpError<'tcx>) -> Self { fn from(kind: InterpError<'tcx>) -> Self {
let capture_backtrace = tls::with_opt(|tcx| { InterpErrorInfo(Box::new(InterpErrorInfoInner {
if let Some(tcx) = tcx { kind,
*Lock::borrow(&tcx.sess.ctfe_backtrace) backtrace: InterpErrorBacktrace::new(),
} else { }))
CtfeBacktrace::Disabled
}
});
let backtrace = match capture_backtrace {
CtfeBacktrace::Disabled => None,
CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())),
CtfeBacktrace::Immediate => {
// Print it now.
let backtrace = Backtrace::force_capture();
print_backtrace(&backtrace);
None
}
};
InterpErrorInfo(Box::new(InterpErrorInfoInner { kind, backtrace }))
} }
} }
/// Error information for when the program we executed turned out not to actually be a valid /// Error information for when the program we executed turned out not to actually be a valid
/// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
/// where we work on generic code or execution does not have all information available. /// where we work on generic code or execution does not have all information available.
#[derive(Debug)]
pub enum InvalidProgramInfo<'tcx> { pub enum InvalidProgramInfo<'tcx> {
/// Resolution can fail if we are in a too generic context. /// Resolution can fail if we are in a too generic context.
TooGeneric, TooGeneric,
@ -174,25 +196,6 @@ pub enum InvalidProgramInfo<'tcx> {
UninitUnsizedLocal, UninitUnsizedLocal,
} }
impl fmt::Display for InvalidProgramInfo<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use InvalidProgramInfo::*;
match self {
TooGeneric => write!(f, "encountered overly generic constant"),
AlreadyReported(_) => {
write!(
f,
"an error has already been reported elsewhere (this should not usually be printed)"
)
}
Layout(ref err) => write!(f, "{err}"),
FnAbiAdjustForForeignAbi(ref err) => write!(f, "{err}"),
SizeOfUnsizedType(ty) => write!(f, "size_of called on unsized type `{ty}`"),
UninitUnsizedLocal => write!(f, "unsized local is used while uninitialized"),
}
}
}
/// Details of why a pointer had to be in-bounds. /// Details of why a pointer had to be in-bounds.
#[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)] #[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
pub enum CheckInAllocMsg { pub enum CheckInAllocMsg {
@ -208,26 +211,25 @@ pub enum CheckInAllocMsg {
InboundsTest, InboundsTest,
} }
impl fmt::Display for CheckInAllocMsg { #[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
/// When this is printed as an error the context looks like this: pub enum InvalidMetaKind {
/// "{msg}{pointer} is a dangling pointer". /// Size of a `[T]` is too big
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { SliceTooBig,
write!( /// Size of a DST is too big
f, TooBig,
"{}", }
match *self {
CheckInAllocMsg::DerefTest => "dereferencing pointer failed: ", impl IntoDiagnosticArg for InvalidMetaKind {
CheckInAllocMsg::MemoryAccessTest => "memory access failed: ", fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
CheckInAllocMsg::PointerArithmeticTest => "out-of-bounds pointer arithmetic: ", DiagnosticArgValue::Str(Cow::Borrowed(match self {
CheckInAllocMsg::OffsetFromTest => "out-of-bounds offset_from: ", InvalidMetaKind::SliceTooBig => "slice_too_big",
CheckInAllocMsg::InboundsTest => "out-of-bounds pointer use: ", InvalidMetaKind::TooBig => "too_big",
} }))
)
} }
} }
/// Details of an access to uninitialized bytes where it is not allowed. /// Details of an access to uninitialized bytes where it is not allowed.
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub struct UninitBytesAccess { pub struct UninitBytesAccess {
/// Range of the original memory access. /// Range of the original memory access.
pub access: AllocRange, pub access: AllocRange,
@ -242,17 +244,32 @@ pub struct ScalarSizeMismatch {
pub data_size: u64, pub data_size: u64,
} }
macro_rules! impl_into_diagnostic_arg_through_debug {
($($ty:ty),*$(,)?) => {$(
impl IntoDiagnosticArg for $ty {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(format!("{self:?}")))
}
}
)*}
}
// These types have nice `Debug` output so we can just use them in diagnostics.
impl_into_diagnostic_arg_through_debug! {
AllocId,
Pointer,
AllocRange,
}
/// Error information for when the program caused Undefined Behavior. /// Error information for when the program caused Undefined Behavior.
pub enum UndefinedBehaviorInfo { #[derive(Debug)]
/// Free-form case. Only for errors that are never caught! pub enum UndefinedBehaviorInfo<'a> {
/// Free-form case. Only for errors that are never caught! Used by miri
Ub(String), Ub(String),
/// Unreachable code was executed. /// Unreachable code was executed.
Unreachable, Unreachable,
/// A slice/array index projection went out-of-bounds. /// A slice/array index projection went out-of-bounds.
BoundsCheckFailed { BoundsCheckFailed { len: u64, index: u64 },
len: u64,
index: u64,
},
/// Something was divided by 0 (x / 0). /// Something was divided by 0 (x / 0).
DivisionByZero, DivisionByZero,
/// Something was "remainded" by 0 (x % 0). /// Something was "remainded" by 0 (x % 0).
@ -263,8 +280,8 @@ pub enum UndefinedBehaviorInfo {
RemainderOverflow, RemainderOverflow,
/// Overflowing inbounds pointer arithmetic. /// Overflowing inbounds pointer arithmetic.
PointerArithOverflow, PointerArithOverflow,
/// Invalid metadata in a wide pointer (using `str` to avoid allocations). /// Invalid metadata in a wide pointer
InvalidMeta(&'static str), InvalidMeta(InvalidMetaKind),
/// Reading a C string that does not end within its allocation. /// Reading a C string that does not end within its allocation.
UnterminatedCString(Pointer), UnterminatedCString(Pointer),
/// Dereferencing a dangling pointer after it got freed. /// Dereferencing a dangling pointer after it got freed.
@ -281,25 +298,13 @@ pub enum UndefinedBehaviorInfo {
/// Using an integer as a pointer in the wrong way. /// Using an integer as a pointer in the wrong way.
DanglingIntPointer(u64, CheckInAllocMsg), DanglingIntPointer(u64, CheckInAllocMsg),
/// Used a pointer with bad alignment. /// Used a pointer with bad alignment.
AlignmentCheckFailed { AlignmentCheckFailed { required: Align, has: Align },
required: Align,
has: Align,
},
/// Writing to read-only memory. /// Writing to read-only memory.
WriteToReadOnly(AllocId), WriteToReadOnly(AllocId),
// Trying to access the data behind a function pointer. /// Trying to access the data behind a function pointer.
DerefFunctionPointer(AllocId), DerefFunctionPointer(AllocId),
// Trying to access the data behind a vtable pointer. /// Trying to access the data behind a vtable pointer.
DerefVTablePointer(AllocId), DerefVTablePointer(AllocId),
/// The value validity check found a problem.
/// Should only be thrown by `validity.rs` and always point out which part of the value
/// is the problem.
ValidationFailure {
/// The "path" to the value in question, e.g. `.0[5].field` for a struct
/// field in the 6th element of an array that is the first element of a tuple.
path: Option<String>,
msg: String,
},
/// Using a non-boolean `u8` as bool. /// Using a non-boolean `u8` as bool.
InvalidBool(u8), InvalidBool(u8),
/// Using a non-character `u32` as character. /// Using a non-character `u32` as character.
@ -320,110 +325,100 @@ pub enum UndefinedBehaviorInfo {
ScalarSizeMismatch(ScalarSizeMismatch), ScalarSizeMismatch(ScalarSizeMismatch),
/// A discriminant of an uninhabited enum variant is written. /// A discriminant of an uninhabited enum variant is written.
UninhabitedEnumVariantWritten, UninhabitedEnumVariantWritten,
/// Validation error.
Validation(ValidationErrorInfo<'a>),
// FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically
// dispatched
/// A custom (free-form) error, created by `err_ub_custom!`.
Custom(crate::error::CustomSubdiagnostic<'a>),
} }
impl fmt::Display for UndefinedBehaviorInfo { #[derive(Debug, Clone, Copy)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { pub enum PointerKind {
use UndefinedBehaviorInfo::*; Ref,
match self { Box,
Ub(msg) => write!(f, "{msg}"), }
Unreachable => write!(f, "entering unreachable code"),
BoundsCheckFailed { ref len, ref index } => { impl IntoDiagnosticArg for PointerKind {
write!(f, "indexing out of bounds: the len is {len} but the index is {index}") fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
} DiagnosticArgValue::Str(
DivisionByZero => write!(f, "dividing by zero"), match self {
RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"), Self::Ref => "ref",
DivisionOverflow => write!(f, "overflow in signed division (dividing MIN by -1)"), Self::Box => "box",
RemainderOverflow => write!(f, "overflow in signed remainder (dividing MIN by -1)"),
PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {msg}"),
UnterminatedCString(p) => write!(
f,
"reading a null-terminated string starting at {p:?} with no null found before end of allocation",
),
PointerUseAfterFree(a) => {
write!(f, "pointer to {a:?} was dereferenced after this allocation got freed")
}
PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size: Size::ZERO, msg } => {
write!(
f,
"{msg}{alloc_id:?} has size {alloc_size}, so pointer at offset {ptr_offset} is out-of-bounds",
alloc_size = alloc_size.bytes(),
)
}
PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size, msg } => write!(
f,
"{msg}{alloc_id:?} has size {alloc_size}, so pointer to {ptr_size} byte{ptr_size_p} starting at offset {ptr_offset} is out-of-bounds",
alloc_size = alloc_size.bytes(),
ptr_size = ptr_size.bytes(),
ptr_size_p = pluralize!(ptr_size.bytes()),
),
DanglingIntPointer(i, msg) => {
write!(
f,
"{msg}{pointer} is a dangling pointer (it has no provenance)",
pointer = Pointer::<Option<AllocId>>::from_addr_invalid(*i),
)
}
AlignmentCheckFailed { required, has } => write!(
f,
"accessing memory with alignment {has}, but alignment {required} is required",
has = has.bytes(),
required = required.bytes()
),
WriteToReadOnly(a) => write!(f, "writing to {a:?} which is read-only"),
DerefFunctionPointer(a) => write!(f, "accessing {a:?} which contains a function"),
DerefVTablePointer(a) => write!(f, "accessing {a:?} which contains a vtable"),
ValidationFailure { path: None, msg } => {
write!(f, "constructing invalid value: {msg}")
}
ValidationFailure { path: Some(path), msg } => {
write!(f, "constructing invalid value at {path}: {msg}")
}
InvalidBool(b) => {
write!(f, "interpreting an invalid 8-bit value as a bool: 0x{b:02x}")
}
InvalidChar(c) => {
write!(f, "interpreting an invalid 32-bit value as a char: 0x{c:08x}")
}
InvalidTag(val) => write!(f, "enum value has invalid tag: {val:x}"),
InvalidFunctionPointer(p) => {
write!(f, "using {p:?} as function pointer but it does not point to a function")
}
InvalidVTablePointer(p) => {
write!(f, "using {p:?} as vtable pointer but it does not point to a vtable")
}
InvalidStr(err) => write!(f, "this string is not valid UTF-8: {err}"),
InvalidUninitBytes(Some((alloc, info))) => write!(
f,
"reading memory at {alloc:?}{access:?}, \
but memory is uninitialized at {uninit:?}, \
and this operation requires initialized memory",
access = info.access,
uninit = info.uninit,
),
InvalidUninitBytes(None) => write!(
f,
"using uninitialized data, but this operation requires initialized memory"
),
DeadLocal => write!(f, "accessing a dead local variable"),
ScalarSizeMismatch(self::ScalarSizeMismatch { target_size, data_size }) => write!(
f,
"scalar size mismatch: expected {target_size} bytes but got {data_size} bytes instead",
),
UninhabitedEnumVariantWritten => {
write!(f, "writing discriminant of an uninhabited enum")
} }
.into(),
)
}
}
#[derive(Debug)]
pub struct ValidationErrorInfo<'tcx> {
pub path: Option<String>,
pub kind: ValidationErrorKind<'tcx>,
}
#[derive(Debug)]
pub enum ExpectedKind {
Reference,
Box,
RawPtr,
InitScalar,
Bool,
Char,
Float,
Int,
FnPtr,
}
impl From<PointerKind> for ExpectedKind {
fn from(x: PointerKind) -> ExpectedKind {
match x {
PointerKind::Box => ExpectedKind::Box,
PointerKind::Ref => ExpectedKind::Reference,
} }
} }
} }
#[derive(Debug)]
pub enum ValidationErrorKind<'tcx> {
PtrToUninhabited { ptr_kind: PointerKind, ty: Ty<'tcx> },
PtrToStatic { ptr_kind: PointerKind },
PtrToMut { ptr_kind: PointerKind },
ExpectedNonPtr { value: String },
MutableRefInConst,
NullFnPtr,
NeverVal,
NullablePtrOutOfRange { range: WrappingRange, max_value: u128 },
PtrOutOfRange { range: WrappingRange, max_value: u128 },
OutOfRange { value: String, range: WrappingRange, max_value: u128 },
UnsafeCell,
UninhabitedVal { ty: Ty<'tcx> },
InvalidEnumTag { value: String },
UninitEnumTag,
UninitStr,
Uninit { expected: ExpectedKind },
UninitVal,
InvalidVTablePtr { value: String },
InvalidMetaSliceTooLarge { ptr_kind: PointerKind },
InvalidMetaTooLarge { ptr_kind: PointerKind },
UnalignedPtr { ptr_kind: PointerKind, required_bytes: u64, found_bytes: u64 },
NullPtr { ptr_kind: PointerKind },
DanglingPtrNoProvenance { ptr_kind: PointerKind, pointer: String },
DanglingPtrOutOfBounds { ptr_kind: PointerKind },
DanglingPtrUseAfterFree { ptr_kind: PointerKind },
InvalidBool { value: String },
InvalidChar { value: String },
InvalidFnPtr { value: String },
}
/// Error information for when the program did something that might (or might not) be correct /// Error information for when the program did something that might (or might not) be correct
/// to do according to the Rust spec, but due to limitations in the interpreter, the /// to do according to the Rust spec, but due to limitations in the interpreter, the
/// operation could not be carried out. These limitations can differ between CTFE and the /// operation could not be carried out. These limitations can differ between CTFE and the
/// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses. /// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses.
#[derive(Debug)]
pub enum UnsupportedOpInfo { pub enum UnsupportedOpInfo {
/// Free-form case. Only for errors that are never caught! /// Free-form case. Only for errors that are never caught!
// FIXME still use translatable diagnostics
Unsupported(String), Unsupported(String),
// //
// The variants below are only reachable from CTFE/const prop, miri will never emit them. // The variants below are only reachable from CTFE/const prop, miri will never emit them.
@ -442,26 +437,9 @@ pub enum UnsupportedOpInfo {
ReadExternStatic(DefId), ReadExternStatic(DefId),
} }
impl fmt::Display for UnsupportedOpInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use UnsupportedOpInfo::*;
match self {
Unsupported(ref msg) => write!(f, "{msg}"),
PartialPointerOverwrite(ptr) => {
write!(f, "unable to overwrite parts of a pointer in memory at {ptr:?}")
}
PartialPointerCopy(ptr) => {
write!(f, "unable to copy parts of a pointer from memory at {ptr:?}")
}
ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes"),
ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({did:?})"),
ReadExternStatic(did) => write!(f, "cannot read from extern static ({did:?})"),
}
}
}
/// Error information for when the program exhausted the resources granted to it /// Error information for when the program exhausted the resources granted to it
/// by the interpreter. /// by the interpreter.
#[derive(Debug)]
pub enum ResourceExhaustionInfo { pub enum ResourceExhaustionInfo {
/// The stack grew too big. /// The stack grew too big.
StackFrameLimitReached, StackFrameLimitReached,
@ -471,47 +449,30 @@ pub enum ResourceExhaustionInfo {
AddressSpaceFull, AddressSpaceFull,
} }
impl fmt::Display for ResourceExhaustionInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ResourceExhaustionInfo::*;
match self {
StackFrameLimitReached => {
write!(f, "reached the configured maximum number of stack frames")
}
MemoryExhausted => {
write!(f, "tried to allocate more memory than available to compiler")
}
AddressSpaceFull => {
write!(f, "there are no more free addresses in the address space")
}
}
}
}
/// A trait to work around not having trait object upcasting.
pub trait AsAny: Any {
fn as_any(&self) -> &dyn Any;
}
impl<T: Any> AsAny for T {
#[inline(always)]
fn as_any(&self) -> &dyn Any {
self
}
}
/// A trait for machine-specific errors (or other "machine stop" conditions). /// A trait for machine-specific errors (or other "machine stop" conditions).
pub trait MachineStopType: AsAny + fmt::Display + Send {} pub trait MachineStopType: Any + fmt::Debug + Send {
/// The diagnostic message for this error
fn diagnostic_message(&self) -> DiagnosticMessage;
/// Add diagnostic arguments by passing name and value pairs to `adder`, which are passed to
/// fluent for formatting the translated diagnostic message.
fn add_args(
self: Box<Self>,
adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>),
);
}
impl dyn MachineStopType { impl dyn MachineStopType {
#[inline(always)] #[inline(always)]
pub fn downcast_ref<T: Any>(&self) -> Option<&T> { pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
self.as_any().downcast_ref() let x: &dyn Any = self;
x.downcast_ref()
} }
} }
#[derive(Debug)]
pub enum InterpError<'tcx> { pub enum InterpError<'tcx> {
/// The program caused undefined behavior. /// The program caused undefined behavior.
UndefinedBehavior(UndefinedBehaviorInfo), UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
/// The program did something the interpreter does not support (some of these *might* be UB /// The program did something the interpreter does not support (some of these *might* be UB
/// but the interpreter is not sure). /// but the interpreter is not sure).
Unsupported(UnsupportedOpInfo), Unsupported(UnsupportedOpInfo),
@ -527,26 +488,6 @@ pub enum InterpError<'tcx> {
pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>; pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
impl fmt::Display for InterpError<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use InterpError::*;
match *self {
Unsupported(ref msg) => write!(f, "{msg}"),
InvalidProgram(ref msg) => write!(f, "{msg}"),
UndefinedBehavior(ref msg) => write!(f, "{msg}"),
ResourceExhaustion(ref msg) => write!(f, "{msg}"),
MachineStop(ref msg) => write!(f, "{msg}"),
}
}
}
// Forward `Debug` to `Display`, so it does not look awful.
impl fmt::Debug for InterpError<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl InterpError<'_> { impl InterpError<'_> {
/// Some errors do string formatting even if the error is never printed. /// Some errors do string formatting even if the error is never printed.
/// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors, /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors,
@ -555,7 +496,7 @@ impl InterpError<'_> {
matches!( matches!(
self, self,
InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_)) InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
| InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure { .. }) | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Validation { .. })
| InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
) )
} }

View file

@ -89,6 +89,30 @@ macro_rules! throw_machine_stop {
($($tt:tt)*) => { do yeet err_machine_stop!($($tt)*) }; ($($tt:tt)*) => { do yeet err_machine_stop!($($tt)*) };
} }
#[macro_export]
macro_rules! err_ub_custom {
($msg:expr $(, $($name:ident = $value:expr),* $(,)?)?) => {{
$(
let ($($name,)*) = ($($value,)*);
)?
err_ub!(Custom(
rustc_middle::error::CustomSubdiagnostic {
msg: || $msg,
add_args: Box::new(move |mut set_arg| {
$($(
set_arg(stringify!($name).into(), rustc_errors::IntoDiagnosticArg::into_diagnostic_arg($name));
)*)?
})
}
))
}};
}
#[macro_export]
macro_rules! throw_ub_custom {
($($tt:tt)*) => { do yeet err_ub_custom!($($tt)*) };
}
mod allocation; mod allocation;
mod error; mod error;
mod pointer; mod pointer;
@ -119,9 +143,10 @@ use crate::ty::{self, Instance, Ty, TyCtxt};
pub use self::error::{ pub use self::error::{
struct_error, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult, struct_error, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult,
EvalToValTreeResult, InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, EvalToValTreeResult, ExpectedKind, InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind,
MachineStopType, ReportedErrorInfo, ResourceExhaustionInfo, ScalarSizeMismatch, InvalidProgramInfo, MachineStopType, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo,
UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo, ScalarSizeMismatch, UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo,
ValidationErrorInfo, ValidationErrorKind,
}; };
pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar}; pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar};

View file

@ -375,7 +375,8 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
#[inline(always)] #[inline(always)]
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
pub fn assert_bits(self, target_size: Size) -> u128 { pub fn assert_bits(self, target_size: Size) -> u128 {
self.to_bits(target_size).unwrap() self.to_bits(target_size)
.unwrap_or_else(|_| panic!("assertion failed: {self:?} fits {target_size:?}"))
} }
pub fn to_bool(self) -> InterpResult<'tcx, bool> { pub fn to_bool(self) -> InterpResult<'tcx, bool> {

View file

@ -15,7 +15,7 @@ use crate::ty::{AdtDef, InstanceDef, ScalarInt, UserTypeAnnotationIndex};
use crate::ty::{GenericArg, InternalSubsts, SubstsRef}; use crate::ty::{GenericArg, InternalSubsts, SubstsRef};
use rustc_data_structures::captures::Captures; use rustc_data_structures::captures::Captures;
use rustc_errors::ErrorGuaranteed; use rustc_errors::{DiagnosticArgValue, DiagnosticMessage, ErrorGuaranteed, IntoDiagnosticArg};
use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
use rustc_hir::{self, GeneratorKind, ImplicitSelfKind}; use rustc_hir::{self, GeneratorKind, ImplicitSelfKind};
@ -1371,55 +1371,61 @@ impl<O> AssertKind<O> {
_ => write!(f, "\"{}\"", self.description()), _ => write!(f, "\"{}\"", self.description()),
} }
} }
}
impl<O: fmt::Debug> fmt::Debug for AssertKind<O> { pub fn diagnostic_message(&self) -> DiagnosticMessage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use crate::fluent_generated::*;
use AssertKind::*; use AssertKind::*;
match self { match self {
BoundsCheck { ref len, ref index } => write!( BoundsCheck { .. } => middle_bounds_check,
f, Overflow(BinOp::Shl, _, _) => middle_assert_shl_overflow,
"index out of bounds: the length is {:?} but the index is {:?}", Overflow(BinOp::Shr, _, _) => middle_assert_shr_overflow,
len, index Overflow(_, _, _) => middle_assert_op_overflow,
), OverflowNeg(_) => middle_assert_overflow_neg,
OverflowNeg(op) => write!(f, "attempt to negate `{:#?}`, which would overflow", op), DivisionByZero(_) => middle_assert_divide_by_zero,
DivisionByZero(op) => write!(f, "attempt to divide `{:#?}` by zero", op), RemainderByZero(_) => middle_assert_remainder_by_zero,
RemainderByZero(op) => write!( ResumedAfterReturn(GeneratorKind::Async(_)) => middle_assert_async_resume_after_return,
f, ResumedAfterReturn(GeneratorKind::Gen) => middle_assert_generator_resume_after_return,
"attempt to calculate the remainder of `{:#?}` with a divisor of zero", ResumedAfterPanic(GeneratorKind::Async(_)) => middle_assert_async_resume_after_panic,
op ResumedAfterPanic(GeneratorKind::Gen) => middle_assert_generator_resume_after_panic,
),
Overflow(BinOp::Add, l, r) => { MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
write!(f, "attempt to compute `{:#?} + {:#?}`, which would overflow", l, r) }
}
pub fn add_args(self, adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>))
where
O: fmt::Debug,
{
use AssertKind::*;
macro_rules! add {
($name: expr, $value: expr) => {
adder($name.into(), $value.into_diagnostic_arg());
};
}
match self {
BoundsCheck { len, index } => {
add!("len", format!("{len:?}"));
add!("index", format!("{index:?}"));
} }
Overflow(BinOp::Sub, l, r) => { Overflow(BinOp::Shl | BinOp::Shr, _, val)
write!(f, "attempt to compute `{:#?} - {:#?}`, which would overflow", l, r) | DivisionByZero(val)
| RemainderByZero(val)
| OverflowNeg(val) => {
add!("val", format!("{val:#?}"));
} }
Overflow(BinOp::Mul, l, r) => { Overflow(binop, left, right) => {
write!(f, "attempt to compute `{:#?} * {:#?}`, which would overflow", l, r) add!("op", binop.to_hir_binop().as_str());
} add!("left", format!("{left:#?}"));
Overflow(BinOp::Div, l, r) => { add!("right", format!("{right:#?}"));
write!(f, "attempt to compute `{:#?} / {:#?}`, which would overflow", l, r)
}
Overflow(BinOp::Rem, l, r) => write!(
f,
"attempt to compute the remainder of `{:#?} % {:#?}`, which would overflow",
l, r
),
Overflow(BinOp::Shr, _, r) => {
write!(f, "attempt to shift right by `{:#?}`, which would overflow", r)
}
Overflow(BinOp::Shl, _, r) => {
write!(f, "attempt to shift left by `{:#?}`, which would overflow", r)
} }
ResumedAfterReturn(_) | ResumedAfterPanic(_) => {}
MisalignedPointerDereference { required, found } => { MisalignedPointerDereference { required, found } => {
write!( add!("required", format!("{required:#?}"));
f, add!("found", format!("{found:#?}"));
"misaligned pointer dereference: address must be a multiple of {:?} but is {:?}",
required, found
)
} }
_ => write!(f, "{}", self.description()),
} }
} }
} }

View file

@ -846,7 +846,7 @@ fn write_allocation_newline(
/// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there /// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there
/// is only one line). Note that your prefix should contain a trailing space as the lines are /// is only one line). Note that your prefix should contain a trailing space as the lines are
/// printed directly after it. /// printed directly after it.
fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
alloc: &Allocation<Prov, Extra, Bytes>, alloc: &Allocation<Prov, Extra, Bytes>,
w: &mut dyn std::fmt::Write, w: &mut dyn std::fmt::Write,

View file

@ -801,7 +801,8 @@ pub enum UnwindAction {
} }
/// Information about an assertion failure. /// Information about an assertion failure.
#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, TypeFoldable, TypeVisitable)] #[derive(Clone, Hash, HashStable, PartialEq, Debug)]
#[derive(TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)]
pub enum AssertKind<O> { pub enum AssertKind<O> {
BoundsCheck { len: O, index: O }, BoundsCheck { len: O, index: O },
Overflow(BinOp, O, O), Overflow(BinOp, O, O),

View file

@ -1,5 +1,6 @@
use rustc_apfloat::ieee::{Double, Single}; use rustc_apfloat::ieee::{Double, Single};
use rustc_apfloat::Float; use rustc_apfloat::Float;
use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use rustc_target::abi::Size; use rustc_target::abi::Size;
use std::fmt; use std::fmt;
@ -113,6 +114,14 @@ impl std::fmt::Debug for ConstInt {
} }
} }
impl IntoDiagnosticArg for ConstInt {
// FIXME this simply uses the Debug impl, but we could probably do better by converting both
// to an inherent method that returns `Cow`.
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(format!("{self:?}").into())
}
}
/// The raw bytes of a simple value. /// The raw bytes of a simple value.
/// ///
/// This is a packed struct in order to allow this type to be optimally embedded in enums /// This is a packed struct in order to allow this type to be optimally embedded in enums

View file

@ -1,8 +1,9 @@
use crate::fluent_generated as fluent; use crate::error::UnsupportedFnAbi;
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use crate::query::TyCtxtAt; use crate::query::TyCtxtAt;
use crate::ty::normalize_erasing_regions::NormalizationError; use crate::ty::normalize_erasing_regions::NormalizationError;
use crate::ty::{self, ReprOptions, Ty, TyCtxt, TypeVisitableExt}; use crate::ty::{self, ReprOptions, Ty, TyCtxt, TypeVisitableExt};
use rustc_error_messages::DiagnosticMessage;
use rustc_errors::{DiagnosticBuilder, Handler, IntoDiagnostic}; use rustc_errors::{DiagnosticBuilder, Handler, IntoDiagnostic};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
@ -14,7 +15,7 @@ use rustc_target::abi::call::FnAbi;
use rustc_target::abi::*; use rustc_target::abi::*;
use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy, Target}; use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy, Target};
use std::cmp::{self}; use std::cmp;
use std::fmt; use std::fmt;
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use std::ops::Bound; use std::ops::Bound;
@ -214,29 +215,29 @@ pub enum LayoutError<'tcx> {
Cycle, Cycle,
} }
impl IntoDiagnostic<'_, !> for LayoutError<'_> { impl<'tcx> LayoutError<'tcx> {
fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> { pub fn diagnostic_message(&self) -> DiagnosticMessage {
let mut diag = handler.struct_fatal(""); use crate::fluent_generated::*;
use LayoutError::*;
match self { match self {
LayoutError::Unknown(ty) => { Unknown(_) => middle_unknown_layout,
diag.set_arg("ty", ty); SizeOverflow(_) => middle_values_too_big,
diag.set_primary_message(fluent::middle_unknown_layout); NormalizationFailure(_, _) => middle_cannot_be_normalized,
} Cycle => middle_cycle,
LayoutError::SizeOverflow(ty) => { }
diag.set_arg("ty", ty); }
diag.set_primary_message(fluent::middle_values_too_big);
} pub fn into_diagnostic(self) -> crate::error::LayoutError<'tcx> {
LayoutError::NormalizationFailure(ty, e) => { use crate::error::LayoutError as E;
diag.set_arg("ty", ty); use LayoutError::*;
diag.set_arg("failure_ty", e.get_type_for_failure()); match self {
diag.set_primary_message(fluent::middle_cannot_be_normalized); Unknown(ty) => E::Unknown { ty },
} SizeOverflow(ty) => E::Overflow { ty },
LayoutError::Cycle => { NormalizationFailure(ty, e) => {
diag.set_primary_message(fluent::middle_cycle); E::NormalizationFailure { ty, failure_ty: e.get_type_for_failure() }
} }
Cycle => E::Cycle,
} }
diag
} }
} }
@ -330,11 +331,8 @@ impl<'tcx> SizeSkeleton<'tcx> {
Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(tail) }) Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(tail) })
} }
_ => bug!( _ => bug!(
"SizeSkeleton::compute({}): layout errored ({}), yet \ "SizeSkeleton::compute({ty}): layout errored ({err:?}), yet \
tail `{}` is not a type parameter or a projection", tail `{tail}` is not a type parameter or a projection",
ty,
err,
tail
), ),
} }
} }
@ -940,12 +938,8 @@ where
TyMaybeWithLayout::Ty(field_ty) => { TyMaybeWithLayout::Ty(field_ty) => {
cx.tcx().layout_of(cx.param_env().and(field_ty)).unwrap_or_else(|e| { cx.tcx().layout_of(cx.param_env().and(field_ty)).unwrap_or_else(|e| {
bug!( bug!(
"failed to get layout for `{}`: {},\n\ "failed to get layout for `{field_ty}`: {e:?},\n\
despite it being a field (#{}) of an existing layout: {:#?}", despite it being a field (#{i}) of an existing layout: {this:#?}",
field_ty,
e,
i,
this
) )
}) })
} }
@ -1262,21 +1256,18 @@ impl From<call::AdjustForForeignAbiError> for FnAbiError<'_> {
} }
} }
impl<'tcx> fmt::Display for FnAbiError<'tcx> { impl<'a, 'b> IntoDiagnostic<'a, !> for FnAbiError<'b> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, !> {
match self { match self {
Self::Layout(err) => err.fmt(f), Self::Layout(e) => e.into_diagnostic().into_diagnostic(handler),
Self::AdjustForForeignAbi(err) => err.fmt(f), Self::AdjustForForeignAbi(call::AdjustForForeignAbiError::Unsupported {
arch,
abi,
}) => UnsupportedFnAbi { arch, abi: abi.name() }.into_diagnostic(handler),
} }
} }
} }
impl IntoDiagnostic<'_, !> for FnAbiError<'_> {
fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> {
handler.struct_fatal(self.to_string())
}
}
// FIXME(eddyb) maybe use something like this for an unified `fn_abi_of`, not // FIXME(eddyb) maybe use something like this for an unified `fn_abi_of`, not
// just for error handling. // just for error handling.
#[derive(Debug)] #[derive(Debug)]

View file

@ -73,7 +73,7 @@ pub(super) fn vtable_allocation_provider<'tcx>(
let ptr_align = tcx.data_layout.pointer_align.abi; let ptr_align = tcx.data_layout.pointer_align.abi;
let vtable_size = ptr_size * u64::try_from(vtable_entries.len()).unwrap(); let vtable_size = ptr_size * u64::try_from(vtable_entries.len()).unwrap();
let mut vtable = Allocation::uninit(vtable_size, ptr_align, /* panic_on_fail */ true).unwrap(); let mut vtable = Allocation::uninit(vtable_size, ptr_align);
// No need to do any alignment checks on the memory accesses below, because we know the // No need to do any alignment checks on the memory accesses below, because we know the
// allocation is correctly aligned as we created it above. Also we're only offsetting by // allocation is correctly aligned as we created it above. Also we're only offsetting by

View file

@ -4,6 +4,7 @@
use either::Right; use either::Right;
use rustc_const_eval::const_eval::CheckAlignment; use rustc_const_eval::const_eval::CheckAlignment;
use rustc_const_eval::ReportErrorExt;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_index::bit_set::BitSet; use rustc_index::bit_set::BitSet;
@ -37,6 +38,7 @@ macro_rules! throw_machine_stop_str {
($($tt:tt)*) => {{ ($($tt:tt)*) => {{
// We make a new local type for it. The type itself does not carry any information, // We make a new local type for it. The type itself does not carry any information,
// but its vtable (for the `MachineStopType` trait) does. // but its vtable (for the `MachineStopType` trait) does.
#[derive(Debug)]
struct Zst; struct Zst;
// Printing this type shows the desired string. // Printing this type shows the desired string.
impl std::fmt::Display for Zst { impl std::fmt::Display for Zst {
@ -44,7 +46,17 @@ macro_rules! throw_machine_stop_str {
write!(f, $($tt)*) write!(f, $($tt)*)
} }
} }
impl rustc_middle::mir::interpret::MachineStopType for Zst {}
impl rustc_middle::mir::interpret::MachineStopType for Zst {
fn diagnostic_message(&self) -> rustc_errors::DiagnosticMessage {
self.to_string().into()
}
fn add_args(
self: Box<Self>,
_: &mut dyn FnMut(std::borrow::Cow<'static, str>, rustc_errors::DiagnosticArgValue<'static>),
) {}
}
throw_machine_stop!(Zst) throw_machine_stop!(Zst)
}}; }};
} }
@ -374,7 +386,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
op op
} }
Err(e) => { Err(e) => {
trace!("get_const failed: {}", e); trace!("get_const failed: {:?}", e.into_kind().debug());
return None; return None;
} }
}; };

View file

@ -9,6 +9,7 @@ use rustc_const_eval::interpret::Immediate;
use rustc_const_eval::interpret::{ use rustc_const_eval::interpret::{
self, InterpCx, InterpResult, LocalValue, MemoryKind, OpTy, Scalar, StackPopCleanup, self, InterpCx, InterpResult, LocalValue, MemoryKind, OpTy, Scalar, StackPopCleanup,
}; };
use rustc_const_eval::ReportErrorExt;
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_hir::HirId; use rustc_hir::HirId;
use rustc_index::bit_set::BitSet; use rustc_index::bit_set::BitSet;
@ -232,7 +233,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
op op
} }
Err(e) => { Err(e) => {
trace!("get_const failed: {}", e); trace!("get_const failed: {:?}", e.into_kind().debug());
return None; return None;
} }
}; };
@ -272,8 +273,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
// dedicated error variants should be introduced instead. // dedicated error variants should be introduced instead.
assert!( assert!(
!error.kind().formatted_string(), !error.kind().formatted_string(),
"const-prop encountered formatting error: {}", "const-prop encountered formatting error: {error:?}",
error
); );
None None
} }

View file

@ -163,7 +163,14 @@ impl<'a, P: std::fmt::Debug> DecorateLint<'a, ()> for AssertLint<P> {
self, self,
diag: &'b mut DiagnosticBuilder<'a, ()>, diag: &'b mut DiagnosticBuilder<'a, ()>,
) -> &'b mut DiagnosticBuilder<'a, ()> { ) -> &'b mut DiagnosticBuilder<'a, ()> {
diag.span_label(self.span(), format!("{:?}", self.panic())); let span = self.span();
let assert_kind = self.panic();
let message = assert_kind.diagnostic_message();
assert_kind.add_args(&mut |name, value| {
diag.set_arg(name, value);
});
diag.span_label(span, message);
diag diag
} }
@ -191,7 +198,7 @@ impl<P> AssertLint<P> {
AssertLint::ArithmeticOverflow(sp, _) | AssertLint::UnconditionalPanic(sp, _) => *sp, AssertLint::ArithmeticOverflow(sp, _) | AssertLint::UnconditionalPanic(sp, _) => *sp,
} }
} }
pub fn panic(&self) -> &AssertKind<P> { pub fn panic(self) -> AssertKind<P> {
match self { match self {
AssertLint::ArithmeticOverflow(_, p) | AssertLint::UnconditionalPanic(_, p) => p, AssertLint::ArithmeticOverflow(_, p) | AssertLint::UnconditionalPanic(_, p) => p,
} }

View file

@ -93,7 +93,7 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
Err(layout_error) => { Err(layout_error) => {
tcx.sess.emit_fatal(Spanned { tcx.sess.emit_fatal(Spanned {
node: layout_error, node: layout_error.into_diagnostic(),
span: tcx.def_span(item_def_id.to_def_id()), span: tcx.def_span(item_def_id.to_def_id()),
}); });
} }
@ -109,12 +109,7 @@ impl<'tcx> LayoutOfHelpers<'tcx> for UnwrapLayoutCx<'tcx> {
type LayoutOfResult = TyAndLayout<'tcx>; type LayoutOfResult = TyAndLayout<'tcx>;
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
span_bug!( span_bug!(span, "`#[rustc_layout(..)]` test resulted in `layout_of({ty}) = Err({err})`",);
span,
"`#[rustc_layout(..)]` test resulted in `layout_of({}) = Err({})`",
ty,
err
);
} }
} }

View file

@ -501,7 +501,15 @@ fn encode_ty<'tcx>(
ty::Array(ty0, len) => { ty::Array(ty0, len) => {
// A<array-length><element-type> // A<array-length><element-type>
let mut s = String::from("A"); let mut s = String::from("A");
let _ = write!(s, "{}", &len.kind().try_to_scalar().unwrap().to_u64().unwrap()); let _ = write!(
s,
"{}",
&len.kind()
.try_to_scalar()
.unwrap()
.to_u64()
.unwrap_or_else(|_| panic!("failed to convert length to u64"))
);
s.push_str(&encode_ty(tcx, *ty0, dict, options)); s.push_str(&encode_ty(tcx, *ty0, dict, options));
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
typeid.push_str(&s); typeid.push_str(&s);
@ -786,7 +794,12 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
} }
ty::Array(ty0, len) => { ty::Array(ty0, len) => {
let len = len.kind().try_to_scalar().unwrap().to_u64().unwrap(); let len = len
.kind()
.try_to_scalar()
.unwrap()
.to_u64()
.unwrap_or_else(|_| panic!("failed to convert length to u64"));
ty = tcx.mk_array(transform_ty(tcx, *ty0, options), len); ty = tcx.mk_array(transform_ty(tcx, *ty0, options), len);
} }

View file

@ -2,7 +2,6 @@ use crate::abi::{self, Abi, Align, FieldsShape, Size};
use crate::abi::{HasDataLayout, TyAbiInterface, TyAndLayout}; use crate::abi::{HasDataLayout, TyAbiInterface, TyAndLayout};
use crate::spec::{self, HasTargetSpec}; use crate::spec::{self, HasTargetSpec};
use rustc_span::Symbol; use rustc_span::Symbol;
use std::fmt;
use std::str::FromStr; use std::str::FromStr;
mod aarch64; mod aarch64;
@ -633,16 +632,6 @@ pub enum AdjustForForeignAbiError {
Unsupported { arch: Symbol, abi: spec::abi::Abi }, Unsupported { arch: Symbol, abi: spec::abi::Abi },
} }
impl fmt::Display for AdjustForForeignAbiError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Unsupported { arch, abi } => {
write!(f, "target architecture {arch:?} does not support `extern {abi}` ABI")
}
}
}
}
impl<'a, Ty> FnAbi<'a, Ty> { impl<'a, Ty> FnAbi<'a, Ty> {
pub fn adjust_for_foreign_abi<C>( pub fn adjust_for_foreign_abi<C>(
&mut self, &mut self,

View file

@ -526,7 +526,7 @@ fn virtual_call_violation_for_method<'tcx>(
// #78372 // #78372
tcx.sess.delay_span_bug( tcx.sess.delay_span_bug(
tcx.def_span(method.def_id), tcx.def_span(method.def_id),
format!("error: {}\n while computing layout for type {:?}", err, ty), format!("error: {err}\n while computing layout for type {ty:?}"),
); );
None None
} }

View file

@ -2,7 +2,7 @@ error: this operation will panic at runtime
--> $DIR/modulo_one.rs:11:5 --> $DIR/modulo_one.rs:11:5
| |
LL | i32::MIN % (-1); // also caught by rustc LL | i32::MIN % (-1); // also caught by rustc
| ^^^^^^^^^^^^^^^ attempt to compute the remainder of `i32::MIN % -1_i32`, which would overflow | ^^^^^^^^^^^^^^^ attempt to compute `i32::MIN % -1_i32`, which would overflow
| |
= note: `#[deny(unconditional_panic)]` on by default = note: `#[deny(unconditional_panic)]` on by default
@ -10,13 +10,13 @@ error: this operation will panic at runtime
--> $DIR/modulo_one.rs:21:5 --> $DIR/modulo_one.rs:21:5
| |
LL | INT_MIN % NEG_ONE; // also caught by rustc LL | INT_MIN % NEG_ONE; // also caught by rustc
| ^^^^^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow | ^^^^^^^^^^^^^^^^^ attempt to compute `i64::MIN % -1_i64`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/modulo_one.rs:22:5 --> $DIR/modulo_one.rs:22:5
| |
LL | INT_MIN % STATIC_NEG_ONE; // ONLY caught by rustc LL | INT_MIN % STATIC_NEG_ONE; // ONLY caught by rustc
| ^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow | ^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute `i64::MIN % -1_i64`, which would overflow
error: any number modulo 1 will be 0 error: any number modulo 1 will be 0
--> $DIR/modulo_one.rs:8:5 --> $DIR/modulo_one.rs:8:5

View file

@ -3,6 +3,8 @@ use std::num::NonZeroU64;
use log::trace; use log::trace;
use rustc_const_eval::ReportErrorExt;
use rustc_errors::DiagnosticMessage;
use rustc_span::{source_map::DUMMY_SP, SpanData, Symbol}; use rustc_span::{source_map::DUMMY_SP, SpanData, Symbol};
use rustc_target::abi::{Align, Size}; use rustc_target::abi::{Align, Size};
@ -83,7 +85,21 @@ impl fmt::Display for TerminationInfo {
} }
} }
impl MachineStopType for TerminationInfo {} impl fmt::Debug for TerminationInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self}")
}
}
impl MachineStopType for TerminationInfo {
fn diagnostic_message(&self) -> DiagnosticMessage {
self.to_string().into()
}
fn add_args(
self: Box<Self>,
_: &mut dyn FnMut(std::borrow::Cow<'static, str>, rustc_errors::DiagnosticArgValue<'static>),
) {}
}
/// Miri specific diagnostics /// Miri specific diagnostics
pub enum NonHaltingDiagnostic { pub enum NonHaltingDiagnostic {
@ -302,8 +318,32 @@ pub fn report_error<'tcx, 'mir>(
let stacktrace = ecx.generate_stacktrace(); let stacktrace = ecx.generate_stacktrace();
let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine); let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine);
e.print_backtrace(); let (e, backtrace) = e.into_parts();
msg.insert(0, e.to_string()); backtrace.print_backtrace();
// We want to dump the allocation if this is `InvalidUninitBytes`. Since `add_args` consumes
// the `InterpError`, we extract the variables it before that.
let extra = match e {
UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some((alloc_id, access)))) => {
Some((alloc_id, access))
}
_ => None
};
// FIXME(fee1-dead), HACK: we want to use the error as title therefore we can just extract the
// label and arguments from the InterpError.
let e = {
let handler = &ecx.tcx.sess.parse_sess.span_diagnostic;
let mut diag = ecx.tcx.sess.struct_allow("");
let msg = e.diagnostic_message();
e.add_args(handler, &mut diag);
let s = handler.eagerly_translate_to_string(msg, diag.args());
diag.cancel();
s
};
msg.insert(0, e);
report_msg( report_msg(
DiagLevel::Error, DiagLevel::Error,
if let Some(title) = title { format!("{title}: {}", msg[0]) } else { msg[0].clone() }, if let Some(title) = title { format!("{title}: {}", msg[0]) } else { msg[0].clone() },
@ -332,15 +372,12 @@ pub fn report_error<'tcx, 'mir>(
} }
// Extra output to help debug specific issues. // Extra output to help debug specific issues.
match e.kind() { if let Some((alloc_id, access)) = extra {
UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some((alloc_id, access)))) => { eprintln!(
eprintln!( "Uninitialized memory occurred at {alloc_id:?}{range:?}, in this allocation:",
"Uninitialized memory occurred at {alloc_id:?}{range:?}, in this allocation:", range = access.uninit,
range = access.uninit, );
); eprintln!("{:?}", ecx.dump_alloc(alloc_id));
eprintln!("{:?}", ecx.dump_alloc(*alloc_id));
}
_ => {}
} }
None None
@ -438,12 +475,15 @@ pub fn report_msg<'tcx>(
// Add visual separator before backtrace. // Add visual separator before backtrace.
err.note(if extra_span { "BACKTRACE (of the first span):" } else { "BACKTRACE:" }); err.note(if extra_span { "BACKTRACE (of the first span):" } else { "BACKTRACE:" });
} }
let (mut err, handler) = err.into_diagnostic().unwrap();
// Add backtrace // Add backtrace
for (idx, frame_info) in stacktrace.iter().enumerate() { for (idx, frame_info) in stacktrace.iter().enumerate() {
let is_local = machine.is_local(frame_info); let is_local = machine.is_local(frame_info);
// No span for non-local frames and the first frame (which is the error site). // No span for non-local frames and the first frame (which is the error site).
if is_local && idx > 0 { if is_local && idx > 0 {
err.span_note(frame_info.span, frame_info.to_string()); err.eager_subdiagnostic(handler, frame_info.as_note(machine.tcx));
} else { } else {
let sm = sess.source_map(); let sm = sess.source_map();
let span = sm.span_to_embeddable_string(frame_info.span); let span = sm.span_to_embeddable_string(frame_info.span);
@ -451,7 +491,7 @@ pub fn report_msg<'tcx>(
} }
} }
err.emit(); handler.emit_diagnostic(&mut err);
} }
impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {

View file

@ -422,8 +422,9 @@ pub fn eval_entry<'tcx>(
let mut ecx = match create_ecx(tcx, entry_id, entry_type, &config) { let mut ecx = match create_ecx(tcx, entry_id, entry_type, &config) {
Ok(v) => v, Ok(v) => v,
Err(err) => { Err(err) => {
err.print_backtrace(); let (kind, backtrace) = err.into_parts();
panic!("Miri initialization error: {}", err.kind()) backtrace.print_backtrace();
panic!("Miri initialization error: {kind:?}")
} }
}; };

View file

@ -164,9 +164,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// We don't give a span -- this isn't actually used directly by the program anyway. // We don't give a span -- this isn't actually used directly by the program anyway.
let const_val = this let const_val = this
.eval_global(cid, None) .eval_global(cid, None)
.unwrap_or_else(|err| panic!("failed to evaluate required Rust item: {path:?}\n{err}")); .unwrap_or_else(|err| panic!("failed to evaluate required Rust item: {path:?}\n{err:?}"));
this.read_scalar(&const_val.into()) this.read_scalar(&const_val.into())
.unwrap_or_else(|err| panic!("failed to read required Rust item: {path:?}\n{err}")) .unwrap_or_else(|err| panic!("failed to read required Rust item: {path:?}\n{err:?}"))
} }
/// Helper function to get a `libc` constant as a `Scalar`. /// Helper function to get a `libc` constant as a `Scalar`.

View file

@ -43,6 +43,7 @@
extern crate rustc_apfloat; extern crate rustc_apfloat;
extern crate rustc_ast; extern crate rustc_ast;
extern crate rustc_errors;
#[macro_use] #[macro_use]
extern crate rustc_middle; extern crate rustc_middle;
extern crate rustc_const_eval; extern crate rustc_const_eval;

View file

@ -10,6 +10,6 @@ fn main() {
unsafe { unsafe {
let a = data.as_mut_ptr(); let a = data.as_mut_ptr();
let b = a.wrapping_offset(1) as *mut _; let b = a.wrapping_offset(1) as *mut _;
copy_nonoverlapping(a, b, 2); //~ ERROR: copy_nonoverlapping called on overlapping ranges copy_nonoverlapping(a, b, 2); //~ ERROR: `copy_nonoverlapping` called on overlapping ranges
} }
} }

View file

@ -1,8 +1,8 @@
error: Undefined Behavior: copy_nonoverlapping called on overlapping ranges error: Undefined Behavior: `copy_nonoverlapping` called on overlapping ranges
--> $DIR/copy_overlapping.rs:LL:CC --> $DIR/copy_overlapping.rs:LL:CC
| |
LL | copy_nonoverlapping(a, b, 2); LL | copy_nonoverlapping(a, b, 2);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ copy_nonoverlapping called on overlapping ranges | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `copy_nonoverlapping` called on overlapping ranges
| |
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

View file

@ -1,8 +1,8 @@
error: Undefined Behavior: out-of-bounds offset_from: ALLOC has size 4, so pointer at offset 10 is out-of-bounds error: Undefined Behavior: out-of-bounds `offset_from`: ALLOC has size 4, so pointer at offset 10 is out-of-bounds
--> $DIR/ptr_offset_from_oob.rs:LL:CC --> $DIR/ptr_offset_from_oob.rs:LL:CC
| |
LL | unsafe { end_ptr.offset_from(end_ptr) }; LL | unsafe { end_ptr.offset_from(end_ptr) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: ALLOC has size 4, so pointer at offset 10 is out-of-bounds | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC has size 4, so pointer at offset 10 is out-of-bounds
| |
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

View file

@ -538,7 +538,7 @@ struct LabelWithTrailingPath {
#[diag(no_crate_example, code = "E0123")] #[diag(no_crate_example, code = "E0123")]
struct LabelWithTrailingNameValue { struct LabelWithTrailingNameValue {
#[label(no_crate_label, foo = "...")] #[label(no_crate_label, foo = "...")]
//~^ ERROR invalid nested attribute //~^ ERROR only `no_span` is a valid nested attribute
span: Span, span: Span,
} }
@ -546,7 +546,7 @@ struct LabelWithTrailingNameValue {
#[diag(no_crate_example, code = "E0123")] #[diag(no_crate_example, code = "E0123")]
struct LabelWithTrailingList { struct LabelWithTrailingList {
#[label(no_crate_label, foo("..."))] #[label(no_crate_label, foo("..."))]
//~^ ERROR invalid nested attribute //~^ ERROR only `no_span` is a valid nested attribute
span: Span, span: Span,
} }

View file

@ -243,7 +243,7 @@ error: invalid nested attribute
LL | #[suggestion(nonsense = "bar")] LL | #[suggestion(nonsense = "bar")]
| ^^^^^^^^ | ^^^^^^^^
| |
= help: only `style`, `code` and `applicability` are valid nested attributes = help: only `no_span`, `style`, `code` and `applicability` are valid nested attributes
error: suggestion without `code = "..."` error: suggestion without `code = "..."`
--> $DIR/diagnostic-derive.rs:234:5 --> $DIR/diagnostic-derive.rs:234:5
@ -257,7 +257,7 @@ error: invalid nested attribute
LL | #[suggestion(msg = "bar")] LL | #[suggestion(msg = "bar")]
| ^^^ | ^^^
| |
= help: only `style`, `code` and `applicability` are valid nested attributes = help: only `no_span`, `style`, `code` and `applicability` are valid nested attributes
error: suggestion without `code = "..."` error: suggestion without `code = "..."`
--> $DIR/diagnostic-derive.rs:243:5 --> $DIR/diagnostic-derive.rs:243:5
@ -335,13 +335,13 @@ error: a diagnostic slug must be the first argument to the attribute
LL | #[label(no_crate_label, foo)] LL | #[label(no_crate_label, foo)]
| ^ | ^
error: invalid nested attribute error: only `no_span` is a valid nested attribute
--> $DIR/diagnostic-derive.rs:540:29 --> $DIR/diagnostic-derive.rs:540:29
| |
LL | #[label(no_crate_label, foo = "...")] LL | #[label(no_crate_label, foo = "...")]
| ^^^ | ^^^
error: invalid nested attribute error: only `no_span` is a valid nested attribute
--> $DIR/diagnostic-derive.rs:548:29 --> $DIR/diagnostic-derive.rs:548:29
| |
LL | #[label(no_crate_label, foo("..."))] LL | #[label(no_crate_label, foo("..."))]

View file

@ -85,7 +85,7 @@ struct F {
#[derive(Subdiagnostic)] #[derive(Subdiagnostic)]
#[label(bug = "...")] #[label(bug = "...")]
//~^ ERROR invalid nested attribute //~^ ERROR only `no_span` is a valid nested attribute
//~| ERROR diagnostic slug must be first argument //~| ERROR diagnostic slug must be first argument
struct G { struct G {
#[primary_span] #[primary_span]
@ -104,7 +104,7 @@ struct H {
#[derive(Subdiagnostic)] #[derive(Subdiagnostic)]
#[label(slug = 4)] #[label(slug = 4)]
//~^ ERROR invalid nested attribute //~^ ERROR only `no_span` is a valid nested attribute
//~| ERROR diagnostic slug must be first argument //~| ERROR diagnostic slug must be first argument
struct J { struct J {
#[primary_span] #[primary_span]
@ -114,7 +114,7 @@ struct J {
#[derive(Subdiagnostic)] #[derive(Subdiagnostic)]
#[label(slug("..."))] #[label(slug("..."))]
//~^ ERROR invalid nested attribute //~^ ERROR only `no_span` is a valid nested attribute
//~| ERROR diagnostic slug must be first argument //~| ERROR diagnostic slug must be first argument
struct K { struct K {
#[primary_span] #[primary_span]
@ -143,7 +143,7 @@ struct M {
#[derive(Subdiagnostic)] #[derive(Subdiagnostic)]
#[label(no_crate_example, code = "...")] #[label(no_crate_example, code = "...")]
//~^ ERROR invalid nested attribute //~^ ERROR only `no_span` is a valid nested attribute
struct N { struct N {
#[primary_span] #[primary_span]
span: Span, span: Span,
@ -152,7 +152,7 @@ struct N {
#[derive(Subdiagnostic)] #[derive(Subdiagnostic)]
#[label(no_crate_example, applicability = "machine-applicable")] #[label(no_crate_example, applicability = "machine-applicable")]
//~^ ERROR invalid nested attribute //~^ ERROR only `no_span` is a valid nested attribute
struct O { struct O {
#[primary_span] #[primary_span]
span: Span, span: Span,
@ -224,7 +224,7 @@ enum T {
enum U { enum U {
#[label(code = "...")] #[label(code = "...")]
//~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute //~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute
//~| ERROR invalid nested attribute //~| ERROR only `no_span` is a valid nested attribute
A { A {
#[primary_span] #[primary_span]
span: Span, span: Span,

View file

@ -26,7 +26,7 @@ error: `#[label = ...]` is not a valid attribute
LL | #[label = "..."] LL | #[label = "..."]
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: invalid nested attribute error: only `no_span` is a valid nested attribute
--> $DIR/subdiagnostic-derive.rs:87:9 --> $DIR/subdiagnostic-derive.rs:87:9
| |
LL | #[label(bug = "...")] LL | #[label(bug = "...")]
@ -44,7 +44,7 @@ error: unexpected literal in nested attribute, expected ident
LL | #[label("...")] LL | #[label("...")]
| ^^^^^ | ^^^^^
error: invalid nested attribute error: only `no_span` is a valid nested attribute
--> $DIR/subdiagnostic-derive.rs:106:9 --> $DIR/subdiagnostic-derive.rs:106:9
| |
LL | #[label(slug = 4)] LL | #[label(slug = 4)]
@ -56,7 +56,7 @@ error: diagnostic slug must be first argument of a `#[label(...)]` attribute
LL | #[label(slug = 4)] LL | #[label(slug = 4)]
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: invalid nested attribute error: only `no_span` is a valid nested attribute
--> $DIR/subdiagnostic-derive.rs:116:9 --> $DIR/subdiagnostic-derive.rs:116:9
| |
LL | #[label(slug("..."))] LL | #[label(slug("..."))]
@ -74,13 +74,13 @@ error: unexpected end of input, unexpected token in nested attribute, expected i
LL | #[label()] LL | #[label()]
| ^ | ^
error: invalid nested attribute error: only `no_span` is a valid nested attribute
--> $DIR/subdiagnostic-derive.rs:145:27 --> $DIR/subdiagnostic-derive.rs:145:27
| |
LL | #[label(no_crate_example, code = "...")] LL | #[label(no_crate_example, code = "...")]
| ^^^^ | ^^^^
error: invalid nested attribute error: only `no_span` is a valid nested attribute
--> $DIR/subdiagnostic-derive.rs:154:27 --> $DIR/subdiagnostic-derive.rs:154:27
| |
LL | #[label(no_crate_example, applicability = "machine-applicable")] LL | #[label(no_crate_example, applicability = "machine-applicable")]
@ -116,7 +116,7 @@ error: `#[bar(...)]` is not a valid attribute
LL | #[bar("...")] LL | #[bar("...")]
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
error: invalid nested attribute error: only `no_span` is a valid nested attribute
--> $DIR/subdiagnostic-derive.rs:225:13 --> $DIR/subdiagnostic-derive.rs:225:13
| |
LL | #[label(code = "...")] LL | #[label(code = "...")]
@ -312,7 +312,7 @@ error: invalid nested attribute
LL | #[multipart_suggestion(no_crate_example, code = "...", applicability = "machine-applicable")] LL | #[multipart_suggestion(no_crate_example, code = "...", applicability = "machine-applicable")]
| ^^^^ | ^^^^
| |
= help: only `style` and `applicability` are valid nested attributes = help: only `no_span`, `style` and `applicability` are valid nested attributes
error: multipart suggestion without any `#[suggestion_part(...)]` fields error: multipart suggestion without any `#[suggestion_part(...)]` fields
--> $DIR/subdiagnostic-derive.rs:540:1 --> $DIR/subdiagnostic-derive.rs:540:1

View file

@ -1,4 +1,4 @@
error[E0080]: evaluation of `Inline::<dyn std::fmt::Debug>::{constant#0}` failed error[E0080]: evaluation of `Inline::<dyn Debug>::{constant#0}` failed
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL --> $SRC_DIR/core/src/mem/mod.rs:LL:COL
| |
= note: size_of called on unsized type `dyn Debug` = note: size_of called on unsized type `dyn Debug`
@ -35,7 +35,7 @@ help: consider relaxing the type parameter's implicit `Sized` bound
LL | impl<T: ?Sized> Inline<T> LL | impl<T: ?Sized> Inline<T>
| ++++++++ | ++++++++
error[E0080]: evaluation of `Inline::<dyn std::fmt::Debug>::{constant#0}` failed error[E0080]: evaluation of `Inline::<dyn Debug>::{constant#0}` failed
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL --> $SRC_DIR/core/src/mem/mod.rs:LL:COL
| |
= note: size_of called on unsized type `dyn Debug` = note: size_of called on unsized type `dyn Debug`

View file

@ -54,11 +54,11 @@ error[E0080]: it is undefined behavior to use this value
LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, size_of::<&u32>()) }; LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, size_of::<&u32>()) };
| ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes | ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
| |
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
HEX_DUMP HEX_DUMP
} }
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value error[E0080]: it is undefined behavior to use this value
--> $DIR/forbidden_slices.rs:30:1 --> $DIR/forbidden_slices.rs:30:1
@ -98,7 +98,7 @@ LL | from_raw_parts(ptr, 1)
error[E0080]: could not evaluate static initializer error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
| |
= note: out-of-bounds offset_from: null pointer is a dangling pointer (it has no provenance) = note: out-of-bounds `offset_from`: null pointer is a dangling pointer (it has no provenance)
| |
note: inside `ptr::const_ptr::<impl *const u32>::sub_ptr` note: inside `ptr::const_ptr::<impl *const u32>::sub_ptr`
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
@ -156,11 +156,11 @@ error[E0080]: it is undefined behavior to use this value
LL | pub static R5: &[u8] = unsafe { LL | pub static R5: &[u8] = unsafe {
| ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes | ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
| |
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
HEX_DUMP HEX_DUMP
} }
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value error[E0080]: it is undefined behavior to use this value
--> $DIR/forbidden_slices.rs:63:1 --> $DIR/forbidden_slices.rs:63:1

View file

@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed
--> $DIR/alloc_intrinsic_errors.rs:9:17 --> $DIR/alloc_intrinsic_errors.rs:9:17
| |
LL | let _ = intrinsics::const_allocate(4, 3) as *mut i32; LL | let _ = intrinsics::const_allocate(4, 3) as *mut i32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ align has to be a power of 2, `3` is not a power of 2 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid align passed to `const_allocate`: 3 is not a power of 2
| |
note: inside `foo` note: inside `foo`
--> $DIR/alloc_intrinsic_errors.rs:9:17 --> $DIR/alloc_intrinsic_errors.rs:9:17

View file

@ -4,11 +4,11 @@
use std::intrinsics; use std::intrinsics;
const FOO: *const i32 = foo(); const FOO: *const i32 = foo();
//~^ ERROR untyped pointers are not allowed in constant //~^ ERROR unsupported untyped pointer in constant
const fn foo() -> &'static i32 { const fn foo() -> &'static i32 {
let t = unsafe { let t = unsafe {
let i = intrinsics::const_allocate(4, 4) as * mut i32; let i = intrinsics::const_allocate(4, 4) as *mut i32;
*i = 20; *i = 20;
i i
}; };

View file

@ -1,8 +1,10 @@
error: untyped pointers are not allowed in constant error: unsupported untyped pointer in constant
--> $DIR/alloc_intrinsic_nontransient_fail.rs:6:1 --> $DIR/alloc_intrinsic_nontransient_fail.rs:6:1
| |
LL | const FOO: *const i32 = foo(); LL | const FOO: *const i32 = foo();
| ^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^
|
= note: memory only reachable via raw pointers is not supported
error: aborting due to previous error error: aborting due to previous error

View file

@ -3,7 +3,7 @@
#![feature(const_mut_refs)] #![feature(const_mut_refs)]
use std::intrinsics; use std::intrinsics;
const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32}; const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 };
//~^ error: untyped pointers are not allowed in constant //~^ error: unsupported untyped pointer in constant
fn main() {} fn main() {}

View file

@ -1,8 +1,10 @@
error: untyped pointers are not allowed in constant error: unsupported untyped pointer in constant
--> $DIR/alloc_intrinsic_untyped.rs:6:1 --> $DIR/alloc_intrinsic_untyped.rs:6:1
| |
LL | const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32}; LL | const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 };
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^
|
= note: memory only reachable via raw pointers is not supported
error: aborting due to previous error error: aborting due to previous error

View file

@ -20,7 +20,7 @@ error[E0080]: evaluation of constant value failed
--> $DIR/dealloc_intrinsic_incorrect_layout.rs:25:5 --> $DIR/dealloc_intrinsic_incorrect_layout.rs:25:5
| |
LL | intrinsics::const_deallocate(ptr, 4, 3); LL | intrinsics::const_deallocate(ptr, 4, 3);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ align has to be a power of 2, `3` is not a power of 2 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid align passed to `const_deallocate`: 3 is not a power of 2
error: aborting due to 4 previous errors error: aborting due to 4 previous errors

View file

@ -35,7 +35,7 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/raw-bytes.rs:44:1 --> $DIR/raw-bytes.rs:44:1
| |
LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(D)>.0: encountered a value of uninhabited type Never | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(D)>.0: encountered a value of uninhabited type `Never`
| |
= 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 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) { = note: the raw bytes of the constant (size: 1, align: 1) {
@ -290,11 +290,11 @@ error[E0080]: it is undefined behavior to use this value
LL | const MYSTR_NO_INIT_ISSUE83182: &MyStr = unsafe { mem::transmute::<&[_], _>(&[&()]) }; LL | const MYSTR_NO_INIT_ISSUE83182: &MyStr = unsafe { mem::transmute::<&[_], _>(&[&()]) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
| |
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: the raw bytes of the constant (size: 8, align: 4) { = note: the raw bytes of the constant (size: 8, align: 4) {
╾ALLOC_ID╼ 01 00 00 00 │ ╾──╼.... ╾ALLOC_ID╼ 01 00 00 00 │ ╾──╼....
} }
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value error[E0080]: it is undefined behavior to use this value
--> $DIR/raw-bytes.rs:152:1 --> $DIR/raw-bytes.rs:152:1
@ -529,11 +529,11 @@ error[E0080]: it is undefined behavior to use this value
LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, mem::size_of::<&u32>()) }; LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, mem::size_of::<&u32>()) };
| ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes | ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
| |
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: the raw bytes of the constant (size: 8, align: 4) { = note: the raw bytes of the constant (size: 8, align: 4) {
╾ALLOC_ID╼ 04 00 00 00 │ ╾──╼.... ╾ALLOC_ID╼ 04 00 00 00 │ ╾──╼....
} }
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value error[E0080]: it is undefined behavior to use this value
--> $DIR/raw-bytes.rs:221:1 --> $DIR/raw-bytes.rs:221:1
@ -574,11 +574,11 @@ error[E0080]: it is undefined behavior to use this value
LL | pub static R5: &[u8] = unsafe { LL | pub static R5: &[u8] = unsafe {
| ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes | ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
| |
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: the raw bytes of the constant (size: 8, align: 4) { = note: the raw bytes of the constant (size: 8, align: 4) {
╾ALLOC_ID╼ 04 00 00 00 │ ╾──╼.... ╾ALLOC_ID╼ 04 00 00 00 │ ╾──╼....
} }
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value error[E0080]: it is undefined behavior to use this value
--> $DIR/raw-bytes.rs:242:1 --> $DIR/raw-bytes.rs:242:1

View file

@ -35,7 +35,7 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/raw-bytes.rs:44:1 --> $DIR/raw-bytes.rs:44:1
| |
LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(D)>.0: encountered a value of uninhabited type Never | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(D)>.0: encountered a value of uninhabited type `Never`
| |
= 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 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) { = note: the raw bytes of the constant (size: 1, align: 1) {
@ -290,11 +290,11 @@ error[E0080]: it is undefined behavior to use this value
LL | const MYSTR_NO_INIT_ISSUE83182: &MyStr = unsafe { mem::transmute::<&[_], _>(&[&()]) }; LL | const MYSTR_NO_INIT_ISSUE83182: &MyStr = unsafe { mem::transmute::<&[_], _>(&[&()]) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
| |
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: the raw bytes of the constant (size: 16, align: 8) { = note: the raw bytes of the constant (size: 16, align: 8) {
╾ALLOC_ID╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ ╾ALLOC_ID╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........
} }
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value error[E0080]: it is undefined behavior to use this value
--> $DIR/raw-bytes.rs:152:1 --> $DIR/raw-bytes.rs:152:1
@ -529,11 +529,11 @@ error[E0080]: it is undefined behavior to use this value
LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, mem::size_of::<&u32>()) }; LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, mem::size_of::<&u32>()) };
| ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes | ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
| |
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: the raw bytes of the constant (size: 16, align: 8) { = note: the raw bytes of the constant (size: 16, align: 8) {
╾ALLOC_ID╼ 08 00 00 00 00 00 00 00 │ ╾──────╼........ ╾ALLOC_ID╼ 08 00 00 00 00 00 00 00 │ ╾──────╼........
} }
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value error[E0080]: it is undefined behavior to use this value
--> $DIR/raw-bytes.rs:221:1 --> $DIR/raw-bytes.rs:221:1
@ -574,11 +574,11 @@ error[E0080]: it is undefined behavior to use this value
LL | pub static R5: &[u8] = unsafe { LL | pub static R5: &[u8] = unsafe {
| ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes | ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
| |
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: the raw bytes of the constant (size: 16, align: 8) { = note: the raw bytes of the constant (size: 16, align: 8) {
╾ALLOC_ID╼ 08 00 00 00 00 00 00 00 │ ╾──────╼........ ╾ALLOC_ID╼ 08 00 00 00 00 00 00 00 │ ╾──────╼........
} }
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error[E0080]: it is undefined behavior to use this value error[E0080]: it is undefined behavior to use this value
--> $DIR/raw-bytes.rs:242:1 --> $DIR/raw-bytes.rs:242:1

View file

@ -86,7 +86,7 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-enum.rs:83:1 --> $DIR/ub-enum.rs:83:1
| |
LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(D)>.0: encountered a value of uninhabited type Never | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(D)>.0: encountered a value of uninhabited type `Never`
| |
= 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 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: $SIZE, align: $ALIGN) { = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
@ -108,7 +108,7 @@ error[E0080]: evaluation of constant value failed
--> $DIR/ub-enum.rs:96:77 --> $DIR/ub-enum.rs:96:77
| |
LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) }; LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) };
| ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(Ok)>.0.1: encountered a value of uninhabited type Never | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(Ok)>.0.1: encountered a value of uninhabited type `Never`
error[E0080]: evaluation of constant value failed error[E0080]: evaluation of constant value failed
--> $DIR/ub-enum.rs:98:77 --> $DIR/ub-enum.rs:98:77

View file

@ -86,7 +86,7 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-enum.rs:83:1 --> $DIR/ub-enum.rs:83:1
| |
LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(D)>.0: encountered a value of uninhabited type Never | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(D)>.0: encountered a value of uninhabited type `Never`
| |
= 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 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: $SIZE, align: $ALIGN) { = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
@ -108,7 +108,7 @@ error[E0080]: evaluation of constant value failed
--> $DIR/ub-enum.rs:96:77 --> $DIR/ub-enum.rs:96:77
| |
LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) }; LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) };
| ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(Ok)>.0.1: encountered a value of uninhabited type Never | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(Ok)>.0.1: encountered a value of uninhabited type `Never`
error[E0080]: evaluation of constant value failed error[E0080]: evaluation of constant value failed
--> $DIR/ub-enum.rs:98:77 --> $DIR/ub-enum.rs:98:77

View file

@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed
--> $DIR/ub-uninhabit.rs:16:35 --> $DIR/ub-uninhabit.rs:16:35
| |
LL | const BAD_BAD_BAD: Bar = unsafe { MaybeUninit { uninit: () }.init }; LL | const BAD_BAD_BAD: Bar = unsafe { MaybeUninit { uninit: () }.init };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a value of uninhabited type Bar | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a value of uninhabited type `Bar`
error[E0080]: it is undefined behavior to use this value error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-uninhabit.rs:19:1 --> $DIR/ub-uninhabit.rs:19:1
@ -19,7 +19,7 @@ error[E0080]: evaluation of constant value failed
--> $DIR/ub-uninhabit.rs:22:42 --> $DIR/ub-uninhabit.rs:22:42
| |
LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { MaybeUninit { uninit: () }.init }; LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { MaybeUninit { uninit: () }.init };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at [0]: encountered a value of uninhabited type Bar | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at [0]: encountered a value of uninhabited type `Bar`
error: aborting due to 3 previous errors error: aborting due to 3 previous errors

View file

@ -28,7 +28,7 @@ error[E0080]: evaluation of constant value failed
--> $DIR/validate_uninhabited_zsts.rs:21:42 --> $DIR/validate_uninhabited_zsts.rs:21:42
| |
LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3]; LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
| ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered a value of uninhabited type empty::Void | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered a value of uninhabited type `Void`
warning: the type `empty::Empty` does not permit zero-initialization warning: the type `empty::Empty` does not permit zero-initialization
--> $DIR/validate_uninhabited_zsts.rs:21:42 --> $DIR/validate_uninhabited_zsts.rs:21:42

View file

@ -28,7 +28,7 @@ error[E0080]: evaluation of constant value failed
--> $DIR/validate_uninhabited_zsts.rs:21:42 --> $DIR/validate_uninhabited_zsts.rs:21:42
| |
LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3]; LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
| ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered a value of uninhabited type empty::Void | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered a value of uninhabited type `Void`
warning: the type `empty::Empty` does not permit zero-initialization warning: the type `empty::Empty` does not permit zero-initialization
--> $DIR/validate_uninhabited_zsts.rs:21:42 --> $DIR/validate_uninhabited_zsts.rs:21:42

View file

@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed
--> $DIR/issue-64506.rs:16:22 --> $DIR/issue-64506.rs:16:22
| |
LL | let x = unsafe { Foo { b: () }.a }; LL | let x = unsafe { Foo { b: () }.a };
| ^^^^^^^^^^^^^^^ constructing invalid value at .inner: encountered a value of uninhabited type AnonPipe | ^^^^^^^^^^^^^^^ constructing invalid value at .inner: encountered a value of uninhabited type `AnonPipe`
error: aborting due to previous error error: aborting due to previous error

View file

@ -4,11 +4,11 @@ error[E0080]: it is undefined behavior to use this value
LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[&()]) }; LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[&()]) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
| |
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
HEX_DUMP HEX_DUMP
} }
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: aborting due to previous error error: aborting due to previous error

View file

@ -3,8 +3,6 @@ error[E0080]: evaluation of constant value failed
| |
= note: unable to turn pointer into raw bytes = note: unable to turn pointer into raw bytes
| |
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
note: inside `std::ptr::read::<u8>` note: inside `std::ptr::read::<u8>`
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
note: inside `ptr::const_ptr::<impl *const u8>::read` note: inside `ptr::const_ptr::<impl *const u8>::read`
@ -14,6 +12,8 @@ note: inside `C`
| |
LL | (&foo as *const _ as *const u8).add(one_and_a_half_pointers).read(); LL | (&foo as *const _ as *const u8).add(one_and_a_half_pointers).read();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: aborting due to previous error error: aborting due to previous error

View file

@ -1,4 +1,4 @@
error[E0080]: evaluation of `<std::string::String as Bar<std::vec::Vec<u32>, std::string::String>>::F` failed error[E0080]: evaluation of `<String as Bar<Vec<u32>, String>>::F` failed
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
| |
= note: calling non-const function `<Vec<u32> as Drop>::drop` = note: calling non-const function `<Vec<u32> as Drop>::drop`

View file

@ -3,6 +3,6 @@
use std::cell::UnsafeCell; use std::cell::UnsafeCell;
const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
//~^ ERROR: untyped pointers are not allowed in constant //~^ ERROR: unsupported untyped pointer in constant
fn main() {} fn main() {}

View file

@ -1,8 +1,10 @@
error: untyped pointers are not allowed in constant error: unsupported untyped pointer in constant
--> $DIR/raw_mutable_const.rs:5:1 --> $DIR/raw_mutable_const.rs:5:1
| |
LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: memory only reachable via raw pointers is not supported
warning: skipping const checks warning: skipping const checks
| |

View file

@ -3,8 +3,6 @@ error[E0080]: evaluation of constant value failed
| |
= note: unable to copy parts of a pointer from memory at ALLOC_ID = note: unable to copy parts of a pointer from memory at ALLOC_ID
| |
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
note: inside `std::ptr::read::<MaybeUninit<MaybeUninit<u8>>>` note: inside `std::ptr::read::<MaybeUninit<MaybeUninit<u8>>>`
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
note: inside `mem::swap_simple::<MaybeUninit<MaybeUninit<u8>>>` note: inside `mem::swap_simple::<MaybeUninit<MaybeUninit<u8>>>`
@ -22,6 +20,8 @@ note: inside `X`
20 | | mem::size_of::<&i32>(), 20 | | mem::size_of::<&i32>(),
21 | | ); 21 | | );
| |_________^ | |_________^
= help: this code performed an operation that depends on the underlying bytes representing a pointer
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
error: aborting due to previous error error: aborting due to previous error

View file

@ -27,31 +27,31 @@ error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:37:14 --> $DIR/offset_from_ub.rs:37:14
| |
LL | unsafe { ptr_offset_from(ptr, ptr) } LL | unsafe { ptr_offset_from(ptr, ptr) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: null pointer is a dangling pointer (it has no provenance) | ^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: null pointer is a dangling pointer (it has no provenance)
error[E0080]: evaluation of constant value failed error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:44:14 --> $DIR/offset_from_ub.rs:44:14
| |
LL | unsafe { ptr_offset_from(ptr2, ptr1) } LL | unsafe { ptr_offset_from(ptr2, ptr1) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: 0x8[noalloc] is a dangling pointer (it has no provenance) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: 0x8[noalloc] is a dangling pointer (it has no provenance)
error[E0080]: evaluation of constant value failed error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:53:14 --> $DIR/offset_from_ub.rs:53:14
| |
LL | unsafe { ptr_offset_from(end_ptr, start_ptr) } LL | unsafe { ptr_offset_from(end_ptr, start_ptr) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc17 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: alloc17 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds
error[E0080]: evaluation of constant value failed error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:62:14 --> $DIR/offset_from_ub.rs:62:14
| |
LL | unsafe { ptr_offset_from(start_ptr, end_ptr) } LL | unsafe { ptr_offset_from(start_ptr, end_ptr) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc20 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: alloc20 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds
error[E0080]: evaluation of constant value failed error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:70:14 --> $DIR/offset_from_ub.rs:70:14
| |
LL | unsafe { ptr_offset_from(end_ptr, end_ptr) } LL | unsafe { ptr_offset_from(end_ptr, end_ptr) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc23 has size 4, so pointer at offset 10 is out-of-bounds | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: alloc23 has size 4, so pointer at offset 10 is out-of-bounds
error[E0080]: evaluation of constant value failed error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:79:14 --> $DIR/offset_from_ub.rs:79:14
@ -86,7 +86,7 @@ LL | unsafe { ptr_offset_from_unsigned(ptr2, ptr1) }
error[E0080]: evaluation of constant value failed error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
| |
= note: out-of-bounds offset_from: null pointer is a dangling pointer (it has no provenance) = note: out-of-bounds `offset_from`: null pointer is a dangling pointer (it has no provenance)
| |
note: inside `ptr::const_ptr::<impl *const u8>::offset_from` note: inside `ptr::const_ptr::<impl *const u8>::offset_from`
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
@ -99,7 +99,7 @@ LL | unsafe { ptr2.offset_from(ptr1) }
error[E0080]: evaluation of constant value failed error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
| |
= note: out-of-bounds offset_from: null pointer is a dangling pointer (it has no provenance) = note: out-of-bounds `offset_from`: null pointer is a dangling pointer (it has no provenance)
| |
note: inside `ptr::const_ptr::<impl *const u8>::offset_from` note: inside `ptr::const_ptr::<impl *const u8>::offset_from`
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL

View file

@ -3,6 +3,6 @@
// could also be allowed. // could also be allowed.
const CONST_RAW: *const Vec<i32> = &Vec::new() as *const _; const CONST_RAW: *const Vec<i32> = &Vec::new() as *const _;
//~^ ERROR untyped pointers are not allowed in constant //~^ ERROR unsupported untyped pointer in constant
fn main() {} fn main() {}

View file

@ -1,8 +1,10 @@
error: untyped pointers are not allowed in constant error: unsupported untyped pointer in constant
--> $DIR/raw-ptr-const.rs:5:1 --> $DIR/raw-ptr-const.rs:5:1
| |
LL | const CONST_RAW: *const Vec<i32> = &Vec::new() as *const _; LL | const CONST_RAW: *const Vec<i32> = &Vec::new() as *const _;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: memory only reachable via raw pointers is not supported
error: aborting due to previous error error: aborting due to previous error

View file

@ -76,37 +76,37 @@ error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:35:36 --> $DIR/issue-8460-const.rs:35:36
| |
LL | assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); LL | assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err());
| ^^^^^^^^^^^^^^^ attempt to compute the remainder of `isize::MIN % -1_isize`, which would overflow | ^^^^^^^^^^^^^^^ attempt to compute `isize::MIN % -1_isize`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:37:36 --> $DIR/issue-8460-const.rs:37:36
| |
LL | assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); LL | assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err());
| ^^^^^^^^^^^^ attempt to compute the remainder of `i8::MIN % -1_i8`, which would overflow | ^^^^^^^^^^^^ attempt to compute `i8::MIN % -1_i8`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:39:36 --> $DIR/issue-8460-const.rs:39:36
| |
LL | assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); LL | assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err());
| ^^^^^^^^^^^^^ attempt to compute the remainder of `i16::MIN % -1_i16`, which would overflow | ^^^^^^^^^^^^^ attempt to compute `i16::MIN % -1_i16`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:41:36 --> $DIR/issue-8460-const.rs:41:36
| |
LL | assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); LL | assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err());
| ^^^^^^^^^^^^^ attempt to compute the remainder of `i32::MIN % -1_i32`, which would overflow | ^^^^^^^^^^^^^ attempt to compute `i32::MIN % -1_i32`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:43:36 --> $DIR/issue-8460-const.rs:43:36
| |
LL | assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); LL | assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err());
| ^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow | ^^^^^^^^^^^^^ attempt to compute `i64::MIN % -1_i64`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:45:36 --> $DIR/issue-8460-const.rs:45:36
| |
LL | assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); LL | assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err());
| ^^^^^^^^^^^^^^ attempt to compute the remainder of `i128::MIN % -1_i128`, which would overflow | ^^^^^^^^^^^^^^ attempt to compute `i128::MIN % -1_i128`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:47:36 --> $DIR/issue-8460-const.rs:47:36

View file

@ -76,37 +76,37 @@ error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:35:36 --> $DIR/issue-8460-const.rs:35:36
| |
LL | assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); LL | assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err());
| ^^^^^^^^^^^^^^^ attempt to compute the remainder of `isize::MIN % -1_isize`, which would overflow | ^^^^^^^^^^^^^^^ attempt to compute `isize::MIN % -1_isize`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:37:36 --> $DIR/issue-8460-const.rs:37:36
| |
LL | assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); LL | assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err());
| ^^^^^^^^^^^^ attempt to compute the remainder of `i8::MIN % -1_i8`, which would overflow | ^^^^^^^^^^^^ attempt to compute `i8::MIN % -1_i8`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:39:36 --> $DIR/issue-8460-const.rs:39:36
| |
LL | assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); LL | assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err());
| ^^^^^^^^^^^^^ attempt to compute the remainder of `i16::MIN % -1_i16`, which would overflow | ^^^^^^^^^^^^^ attempt to compute `i16::MIN % -1_i16`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:41:36 --> $DIR/issue-8460-const.rs:41:36
| |
LL | assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); LL | assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err());
| ^^^^^^^^^^^^^ attempt to compute the remainder of `i32::MIN % -1_i32`, which would overflow | ^^^^^^^^^^^^^ attempt to compute `i32::MIN % -1_i32`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:43:36 --> $DIR/issue-8460-const.rs:43:36
| |
LL | assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); LL | assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err());
| ^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow | ^^^^^^^^^^^^^ attempt to compute `i64::MIN % -1_i64`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:45:36 --> $DIR/issue-8460-const.rs:45:36
| |
LL | assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); LL | assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err());
| ^^^^^^^^^^^^^^ attempt to compute the remainder of `i128::MIN % -1_i128`, which would overflow | ^^^^^^^^^^^^^^ attempt to compute `i128::MIN % -1_i128`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:47:36 --> $DIR/issue-8460-const.rs:47:36

View file

@ -76,37 +76,37 @@ error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:35:36 --> $DIR/issue-8460-const.rs:35:36
| |
LL | assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); LL | assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err());
| ^^^^^^^^^^^^^^^ attempt to compute the remainder of `isize::MIN % -1_isize`, which would overflow | ^^^^^^^^^^^^^^^ attempt to compute `isize::MIN % -1_isize`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:37:36 --> $DIR/issue-8460-const.rs:37:36
| |
LL | assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); LL | assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err());
| ^^^^^^^^^^^^ attempt to compute the remainder of `i8::MIN % -1_i8`, which would overflow | ^^^^^^^^^^^^ attempt to compute `i8::MIN % -1_i8`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:39:36 --> $DIR/issue-8460-const.rs:39:36
| |
LL | assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); LL | assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err());
| ^^^^^^^^^^^^^ attempt to compute the remainder of `i16::MIN % -1_i16`, which would overflow | ^^^^^^^^^^^^^ attempt to compute `i16::MIN % -1_i16`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:41:36 --> $DIR/issue-8460-const.rs:41:36
| |
LL | assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); LL | assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err());
| ^^^^^^^^^^^^^ attempt to compute the remainder of `i32::MIN % -1_i32`, which would overflow | ^^^^^^^^^^^^^ attempt to compute `i32::MIN % -1_i32`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:43:36 --> $DIR/issue-8460-const.rs:43:36
| |
LL | assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); LL | assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err());
| ^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow | ^^^^^^^^^^^^^ attempt to compute `i64::MIN % -1_i64`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:45:36 --> $DIR/issue-8460-const.rs:45:36
| |
LL | assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); LL | assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err());
| ^^^^^^^^^^^^^^ attempt to compute the remainder of `i128::MIN % -1_i128`, which would overflow | ^^^^^^^^^^^^^^ attempt to compute `i128::MIN % -1_i128`, which would overflow
error: this operation will panic at runtime error: this operation will panic at runtime
--> $DIR/issue-8460-const.rs:47:36 --> $DIR/issue-8460-const.rs:47:36

View file

@ -47,7 +47,7 @@ error[E0080]: could not evaluate static initializer
--> $DIR/uninhabited-static.rs:12:31 --> $DIR/uninhabited-static.rs:12:31
| |
LL | static VOID2: Void = unsafe { std::mem::transmute(()) }; LL | static VOID2: Void = unsafe { std::mem::transmute(()) };
| ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a value of uninhabited type Void | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a value of uninhabited type `Void`
warning: the type `Void` does not permit zero-initialization warning: the type `Void` does not permit zero-initialization
--> $DIR/uninhabited-static.rs:12:31 --> $DIR/uninhabited-static.rs:12:31
@ -66,7 +66,7 @@ error[E0080]: could not evaluate static initializer
--> $DIR/uninhabited-static.rs:16:32 --> $DIR/uninhabited-static.rs:16:32
| |
LL | static NEVER2: Void = unsafe { std::mem::transmute(()) }; LL | static NEVER2: Void = unsafe { std::mem::transmute(()) };
| ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a value of uninhabited type Void | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a value of uninhabited type `Void`
warning: the type `Void` does not permit zero-initialization warning: the type `Void` does not permit zero-initialization
--> $DIR/uninhabited-static.rs:16:32 --> $DIR/uninhabited-static.rs:16:32