summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_const_eval
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_const_eval')
-rw-r--r--compiler/rustc_const_eval/messages.ftl400
-rw-r--r--compiler/rustc_const_eval/src/const_eval/error.rs280
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs106
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs132
-rw-r--r--compiler/rustc_const_eval/src/const_eval/mod.rs94
-rw-r--r--compiler/rustc_const_eval/src/const_eval/valtrees.rs7
-rw-r--r--compiler/rustc_const_eval/src/errors.rs688
-rw-r--r--compiler/rustc_const_eval/src/interpret/cast.rs49
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs47
-rw-r--r--compiler/rustc_const_eval/src/interpret/intern.rs27
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs106
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs63
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/operator.rs68
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs13
-rw-r--r--compiler/rustc_const_eval/src/interpret/projection.rs5
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs28
-rw-r--r--compiler/rustc_const_eval/src/interpret/terminator.rs57
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs217
-rw-r--r--compiler/rustc_const_eval/src/lib.rs13
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs75
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs213
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs15
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/resolver.rs8
-rw-r--r--compiler/rustc_const_eval/src/transform/promote_consts.rs33
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs96
-rw-r--r--compiler/rustc_const_eval/src/util/check_validity_requirement.rs15
-rw-r--r--compiler/rustc_const_eval/src/util/mod.rs26
-rw-r--r--compiler/rustc_const_eval/src/util/type_name.rs1
29 files changed, 2009 insertions, 877 deletions
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index 7d56cf0aa..e99005316 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -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 =
cannot borrow here, since the borrowed element may contain interior mutability
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
.help = to fix this, the value can be extracted to a separate `static` item and then referenced
.teach_note =
@@ -10,19 +144,166 @@ const_eval_interior_mutable_data_refer =
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.
+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 =
+ 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.
+ If your compilation actually takes a long time, you can safely allow the lint.
+ .label = the const evaluator is currently interpreting this expression
+ .help = the constant being evaluated
+
+const_eval_match_eq_non_const = cannot match on `{$ty}` in {const_eval_const_context}s
+ .note = `{$ty}` cannot be compared in compile-time, and therefore cannot be used in `match`es
+
const_eval_max_num_nodes_in_const = maximum number of nodes exceeded in constant {$global_const_id}
+const_eval_memory_access_test = memory access failed
+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 {$kind}s
+ 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 =
- 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 =
- 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_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 =
pointers cannot be reliably compared during const eval
.note = see issue #53020 <https://github.com/rust-lang/rust/issues/53020> for more information
@@ -32,8 +313,36 @@ const_eval_raw_ptr_to_int =
.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
+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 =
- {$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
.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.
@@ -41,27 +350,34 @@ const_eval_static_access =
const_eval_thread_local_access =
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 =
- allocations are not allowed in {$kind}s
- .label = allocation not allowed in {$kind}s
+ allocations are not allowed in {const_eval_const_context}s
+ .label = allocation not allowed in {const_eval_const_context}s
.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.
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 =
- 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 =
- 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
safety since holding multiple mutable references to shared data is not allowed.
@@ -69,7 +385,7 @@ const_eval_unallowed_mutable_refs =
If you really want global mutable state, try using static mut or a global UnsafeCell.
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 =
References in statics and constants may only refer to immutable values.
@@ -83,9 +399,59 @@ const_eval_unallowed_mutable_refs_raw =
const_eval_unallowed_op_in_const_context =
{$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_in_stable =
const-stable function cannot use `#[feature({$gate})]`
.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
+
+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
diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs
index c591ff75a..7890d878d 100644
--- a/compiler/rustc_const_eval/src/const_eval/error.rs
+++ b/compiler/rustc_const_eval/src/const_eval/error.rs
@@ -1,17 +1,15 @@
-use std::error::Error;
-use std::fmt;
+use std::mem;
-use rustc_errors::Diagnostic;
+use rustc_errors::{DiagnosticArgValue, DiagnosticMessage, IntoDiagnostic, IntoDiagnosticArg};
use rustc_middle::mir::AssertKind;
-use rustc_middle::query::TyCtxtAt;
+use rustc_middle::ty::TyCtxt;
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 crate::interpret::{
- struct_error, ErrorHandled, FrameInfo, InterpError, InterpErrorInfo, Machine, MachineStopType,
- UnsupportedOpInfo,
-};
+use crate::errors::{self, FrameNote, ReportErrorExt};
+use crate::interpret::{ErrorHandled, InterpError, InterpErrorInfo, Machine, MachineStopType};
/// The CTFE machine has some custom error kinds.
#[derive(Clone, Debug)]
@@ -23,7 +21,35 @@ pub enum ConstEvalErrKind {
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.
// `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 {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- use self::ConstEvalErrKind::*;
- match self {
- ConstAccessesStatic => write!(f, "constant accesses static"),
- ModifiedGlobal => {
- write!(f, "modifying a static's initial value from another static's initializer")
- }
- 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 {}
+pub fn get_span_and_frames<'tcx, 'mir, M: Machine<'mir, 'tcx>>(
+ ecx: &InterpCx<'mir, 'tcx, M>,
+) -> (Span, Vec<errors::FrameNote>)
+where
+ 'tcx: 'mir,
+{
+ 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);
-/// When const-evaluation errors, this type is constructed with the resulting information,
-/// and then used to emit the error as a lint or hard error.
-#[derive(Debug)]
-pub(super) struct ConstEvalErr<'tcx> {
- pub span: Span,
- pub error: InterpError<'tcx>,
- pub stacktrace: Vec<FrameInfo<'tcx>>,
-}
+ let mut frames = Vec::new();
-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 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);
}
- _ => {}
- }
- // 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),
- );
- }
- }
- };
+ };
- let mut last_frame = None;
- let mut times = 0;
- 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);
+ let mut last_frame: Option<errors::FrameNote> = None;
+ for frame_info in &stacktrace {
+ let frame = frame_info.as_note(*ecx.tcx);
+ match last_frame.as_mut() {
+ Some(last_frame)
+ if last_frame.span == frame.span
+ && last_frame.where_ == frame.where_
+ && last_frame.instance == frame.instance =>
+ {
+ last_frame.times += 1;
+ }
+ Some(last_frame) => {
+ add_frame(mem::replace(last_frame, frame));
+ }
+ None => {
last_frame = Some(frame);
- times = 0;
}
}
- flush_last_line(last_frame, times);
}
- // Let the caller attach any additional information it wants.
- decorate(err);
+ if let Some(frame) = last_frame {
+ add_frame(frame);
+ }
}
- /// Create a diagnostic for this const eval error.
- ///
- /// 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,
- /// after which the diagnostic is emitted.
- ///
- /// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
- /// (Except that for some errors, we ignore all that -- see `must_error` below.)
- #[instrument(skip(self, tcx, decorate), level = "debug")]
- pub(super) fn report_decorated(
- &self,
- tcx: TyCtxtAt<'tcx>,
- message: &str,
- decorate: impl FnOnce(&mut Diagnostic),
- ) -> ErrorHandled {
- debug!("self.error: {:?}", self.error);
- // Special handling for certain errors
- match &self.error {
- // Don't emit a new diagnostic for these errors
- err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
- ErrorHandled::TooGeneric
- }
- err_inval!(AlreadyReported(error_reported)) => ErrorHandled::Reported(*error_reported),
- err_inval!(Layout(LayoutError::SizeOverflow(_))) => {
- // We must *always* hard error on these, even if the caller wants just a lint.
- // The `message` makes little sense here, this is a more serious error than the
- // caller thinks anyway.
- // See <https://github.com/rust-lang/rust/pull/63152>.
- let mut err = struct_error(tcx, &self.error.to_string());
- self.decorate(&mut err, decorate);
- ErrorHandled::Reported(err.emit().into())
- }
- _ => {
- // Report as hard error.
- let mut err = struct_error(tcx, message);
- err.span_label(self.span, self.error.to_string());
- self.decorate(&mut err, decorate);
- ErrorHandled::Reported(err.emit().into())
+ (span, frames)
+}
+
+/// Create a diagnostic for a const eval error.
+///
+/// This will use the `mk` function for creating the error which will get passed labels according to
+/// the `InterpError` and the span and a stacktrace of current execution according to
+/// `get_span_and_frames`.
+pub(super) fn report<'tcx, C, F, E>(
+ tcx: TyCtxt<'tcx>,
+ error: InterpError<'tcx>,
+ span: Option<Span>,
+ get_span_and_frames: C,
+ mk: F,
+) -> ErrorHandled
+where
+ C: FnOnce() -> (Span, Vec<FrameNote>),
+ F: FnOnce(Span, Vec<FrameNote>) -> E,
+ E: IntoDiagnostic<'tcx, ErrorGuaranteed>,
+{
+ // Special handling for certain errors
+ match error {
+ // Don't emit a new diagnostic for these errors
+ err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
+ ErrorHandled::TooGeneric
+ }
+ err_inval!(AlreadyReported(error_reported)) => ErrorHandled::Reported(error_reported),
+ err_inval!(Layout(layout_error @ LayoutError::SizeOverflow(_))) => {
+ // We must *always* hard error on these, even if the caller wants just a lint.
+ // The `message` makes little sense here, this is a more serious error than the
+ // caller thinks anyway.
+ // See <https://github.com/rust-lang/rust/pull/63152>.
+ let (our_span, frames) = get_span_and_frames();
+ let span = span.unwrap_or(our_span);
+ let mut err =
+ tcx.sess.create_err(Spanned { span, node: layout_error.into_diagnostic() });
+ err.code(rustc_errors::error_code!(E0080));
+ 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())
}
}
}
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index 046d20529..417ab78fd 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -1,12 +1,12 @@
use crate::const_eval::CheckAlignment;
-use std::borrow::Cow;
+use crate::errors::ConstEvalError;
use either::{Left, Right};
use rustc_hir::def::DefKind;
use rustc_middle::mir;
-use rustc_middle::mir::interpret::ErrorHandled;
-use rustc_middle::mir::pretty::display_allocation;
+use rustc_middle::mir::interpret::{ErrorHandled, InterpErrorInfo};
+use rustc_middle::mir::pretty::write_allocation_bytes;
use rustc_middle::traits::Reveal;
use rustc_middle::ty::layout::LayoutOf;
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_target::abi::{self, Abi};
-use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr};
+use super::{CanAccessStatics, CompileTimeEvalContext, CompileTimeInterpreter};
+use crate::errors;
use crate::interpret::eval_nullary_intrinsic;
use crate::interpret::{
intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId,
@@ -22,10 +23,6 @@ use crate::interpret::{
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
fn eval_body_using_ecx<'mir, 'tcx>(
ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
@@ -96,14 +93,14 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>(
tcx: TyCtxt<'tcx>,
root_span: Span,
param_env: ty::ParamEnv<'tcx>,
- can_access_statics: bool,
+ can_access_statics: CanAccessStatics,
) -> CompileTimeEvalContext<'mir, 'tcx> {
debug!("mk_eval_cx: {:?}", param_env);
InterpCx::new(
tcx,
root_span,
param_env,
- CompileTimeInterpreter::new(tcx.const_eval_limit(), can_access_statics, CheckAlignment::No),
+ CompileTimeInterpreter::new(can_access_statics, CheckAlignment::No),
)
}
@@ -210,7 +207,7 @@ pub(crate) fn turn_into_const_value<'tcx>(
tcx,
tcx.def_span(key.value.instance.def_id()),
key.param_env,
- /*can_access_statics:*/ is_static,
+ CanAccessStatics::from(is_static),
);
let mplace = ecx.raw_const_to_mplace(constant).expect(
@@ -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| {
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 },
+ )
});
}
@@ -306,8 +309,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
// Statics (and promoteds inside statics) may access other statics, because unlike consts
// they do not have to behave "as if" they were evaluated at runtime.
CompileTimeInterpreter::new(
- tcx.const_eval_limit(),
- /*can_access_statics:*/ is_static,
+ CanAccessStatics::from(is_static),
if tcx.sess.opts.unstable_opts.extra_const_ub_checks {
CheckAlignment::Error
} else {
@@ -319,9 +321,11 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
let res = ecx.load_mir(cid.instance.def, cid.promoted);
match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) {
Err(error) => {
- let err = ConstEvalErr::new(&ecx, error, None);
- let msg = if is_static {
- Cow::from("could not evaluate static initializer")
+ let (error, backtrace) = error.into_parts();
+ backtrace.print_backtrace();
+
+ let (kind, instance) = if is_static {
+ ("static", String::new())
} else {
// If the current item has generics, we'd like to enrich the message with the
// instance and its substs: to show the actual compile-time values, in addition to
@@ -329,19 +333,29 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
let instance = &key.value.instance;
if !instance.substs.is_empty() {
let instance = with_no_trimmed_paths!(instance.to_string());
- let msg = format!("evaluation of `{}` failed", instance);
- Cow::from(msg)
+ ("const_with_path", instance)
} 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) => {
// Since evaluation had no errors, validate the resulting constant.
// 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 inner = false;
while let Some((mplace, path)) = ref_tracking.todo.pop() {
@@ -358,23 +372,37 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
}
};
let alloc_id = mplace.ptr.provenance.unwrap();
+
+ // Validation failed, report an error. This is always a hard error.
if let Err(error) = validation {
- // Validation failed, report an error. This is always a hard error.
- let err = ConstEvalErr::new(&ecx, error, None);
- Err(err.report_decorated(
- ecx.tcx,
- "it is undefined behavior to use this value",
- |diag| {
- if matches!(err.error, InterpError::UndefinedBehavior(_)) {
- diag.note(NOTE_ON_UNDEFINED_BEHAVIOR_ERROR);
- }
- diag.note(format!(
- "the raw bytes of the constant ({}",
- display_allocation(
- *ecx.tcx,
- ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner()
- )
- ));
+ let (error, backtrace) = error.into_parts();
+ backtrace.print_backtrace();
+
+ let ub_note = matches!(error, InterpError::UndefinedBehavior(_)).then(|| {});
+
+ let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner();
+ let mut bytes = String::new();
+ if alloc.size() != abi::Size::ZERO {
+ bytes = "\n".into();
+ // FIXME(translation) there might be pieces that are translatable.
+ write_allocation_bytes(*ecx.tcx, alloc, &mut bytes, " ").unwrap();
+ }
+ let raw_bytes = errors::RawBytesNote {
+ 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 {
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 58b5755af..f9f645af4 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -16,25 +16,37 @@ use std::fmt;
use rustc_ast::Mutability;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::AssertMessage;
-use rustc_session::Limit;
use rustc_span::symbol::{sym, Symbol};
use rustc_target::abi::{Align, Size};
use rustc_target::spec::abi::Abi as CallAbi;
+use crate::errors::{LongRunning, LongRunningWarn};
use crate::interpret::{
self, compile_time_machine, AllocId, ConstAllocation, FnVal, Frame, ImmTy, InterpCx,
InterpResult, OpTy, PlaceTy, Pointer, Scalar,
};
+use crate::{errors, fluent_generated as fluent};
use super::error::*;
+/// When hitting this many interpreted terminators we emit a deny by default lint
+/// that notfies the user that their constant takes a long time to evaluate. If that's
+/// what they intended, they can just allow the lint.
+const LINT_TERMINATOR_LIMIT: usize = 2_000_000;
+/// The limit used by `-Z tiny-const-eval-limit`. This smaller limit is useful for internal
+/// tests not needing to run 30s or more to show some behaviour.
+const TINY_LINT_TERMINATOR_LIMIT: usize = 20;
+/// After this many interpreted terminators, we start emitting progress indicators at every
+/// power of two of interpreted terminators.
+const PROGRESS_INDICATOR_START: usize = 4_000_000;
+
/// Extra machine state for CTFE, and the Machine instance
pub struct CompileTimeInterpreter<'mir, 'tcx> {
- /// For now, the number of terminators that can be evaluated before we throw a resource
- /// exhaustion error.
+ /// The number of terminators that have been evaluated.
///
- /// Setting this to `0` disables the limit and allows the interpreter to run forever.
- pub(super) steps_remaining: usize,
+ /// This is used to produce lints informing the user that the compiler is not stuck.
+ /// Set to `usize::MAX` to never report anything.
+ pub(super) num_evaluated_steps: usize,
/// The virtual call stack.
pub(super) stack: Vec<Frame<'mir, 'tcx, AllocId, ()>>,
@@ -45,7 +57,7 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
/// * Interning makes everything outside of statics immutable.
/// * Pointers to allocations inside of statics can never leak outside, to a non-static global.
/// This boolean here controls the second part.
- pub(super) can_access_statics: bool,
+ pub(super) can_access_statics: CanAccessStatics,
/// Whether to check alignment during evaluation.
pub(super) check_alignment: CheckAlignment,
@@ -71,14 +83,25 @@ impl CheckAlignment {
}
}
+#[derive(Copy, Clone, PartialEq)]
+pub(crate) enum CanAccessStatics {
+ No,
+ Yes,
+}
+
+impl From<bool> for CanAccessStatics {
+ fn from(value: bool) -> Self {
+ if value { Self::Yes } else { Self::No }
+ }
+}
+
impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
pub(crate) fn new(
- const_eval_limit: Limit,
- can_access_statics: bool,
+ can_access_statics: CanAccessStatics,
check_alignment: CheckAlignment,
) -> Self {
CompileTimeInterpreter {
- steps_remaining: const_eval_limit.0,
+ num_evaluated_steps: 0,
stack: Vec::new(),
can_access_statics,
check_alignment,
@@ -247,7 +270,10 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
let target_align = self.read_scalar(&args[1])?.to_target_usize(self)?;
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) {
@@ -353,15 +379,18 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
"`alignment_check_failed` called when no alignment check requested"
),
CheckAlignment::FutureIncompat => {
- let err = ConstEvalErr::new(ecx, err, None);
- ecx.tcx.struct_span_lint_hir(
+ let (_, backtrace) = err.into_parts();
+ backtrace.print_backtrace();
+ let (span, frames) = super::get_span_and_frames(&ecx);
+
+ ecx.tcx.emit_spanned_lint(
INVALID_ALIGNMENT,
ecx.stack().iter().find_map(|frame| frame.lint_root()).unwrap_or(CRATE_HIR_ID),
- err.span,
- err.error.to_string(),
- |db| {
- err.decorate(db, |_| {});
- db
+ span,
+ errors::AlignmentCheckFailed {
+ has: has.bytes(),
+ required: required.bytes(),
+ frames,
},
);
Ok(())
@@ -475,7 +504,12 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
let align = match Align::from_bytes(align) {
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(
@@ -493,7 +527,12 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
let size = Size::from_bytes(size);
let align = match Align::from_bytes(align) {
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,
@@ -569,13 +608,54 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
fn increment_const_eval_counter(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
// The step limit has already been hit in a previous call to `increment_const_eval_counter`.
- if ecx.machine.steps_remaining == 0 {
- return Ok(());
- }
- ecx.machine.steps_remaining -= 1;
- if ecx.machine.steps_remaining == 0 {
- throw_exhaust!(StepLimitReached)
+ if let Some(new_steps) = ecx.machine.num_evaluated_steps.checked_add(1) {
+ let (limit, start) = if ecx.tcx.sess.opts.unstable_opts.tiny_const_eval_limit {
+ (TINY_LINT_TERMINATOR_LIMIT, TINY_LINT_TERMINATOR_LIMIT)
+ } else {
+ (LINT_TERMINATOR_LIMIT, PROGRESS_INDICATOR_START)
+ };
+
+ ecx.machine.num_evaluated_steps = new_steps;
+ // By default, we have a *deny* lint kicking in after some time
+ // to ensure `loop {}` doesn't just go forever.
+ // In case that lint got reduced, in particular for `--cap-lint` situations, we also
+ // have a hard warning shown every now and then for really long executions.
+ if new_steps == limit {
+ // By default, we stop after a million steps, but the user can disable this lint
+ // to be able to run until the heat death of the universe or power loss, whichever
+ // comes first.
+ let hir_id = ecx.best_lint_scope();
+ let is_error = ecx
+ .tcx
+ .lint_level_at_node(
+ rustc_session::lint::builtin::LONG_RUNNING_CONST_EVAL,
+ hir_id,
+ )
+ .0
+ .is_error();
+ let span = ecx.cur_span();
+ ecx.tcx.emit_spanned_lint(
+ rustc_session::lint::builtin::LONG_RUNNING_CONST_EVAL,
+ hir_id,
+ span,
+ LongRunning { item_span: ecx.tcx.span },
+ );
+ // If this was a hard error, don't bother continuing evaluation.
+ if is_error {
+ let guard = ecx
+ .tcx
+ .sess
+ .delay_span_bug(span, "The deny lint should have already errored");
+ throw_inval!(AlreadyReported(guard.into()));
+ }
+ } else if new_steps > start && new_steps.is_power_of_two() {
+ // Only report after a certain number of terminators have been evaluated and the
+ // current number of evaluated terminators is a power of 2. The latter gives us a cheap
+ // way to implement exponential backoff.
+ let span = ecx.cur_span();
+ ecx.tcx.sess.emit_warning(LongRunningWarn { span, item_span: ecx.tcx.span });
+ }
}
Ok(())
@@ -634,7 +714,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
}
} else {
// Read access. These are usually allowed, with some exceptions.
- if machine.can_access_statics {
+ if machine.can_access_statics == CanAccessStatics::Yes {
// Machine configuration allows us read from anything (e.g., `static` initializer).
Ok(())
} else if static_def_id.is_some() {
diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs
index 05be45fef..a3064b53d 100644
--- a/compiler/rustc_const_eval/src/const_eval/mod.rs
+++ b/compiler/rustc_const_eval/src/const_eval/mod.rs
@@ -1,14 +1,10 @@
// Not in interpret to make sure we do not use private implementation details
use crate::errors::MaxNumNodesInConstErr;
-use crate::interpret::{
- intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MemPlaceMeta,
- Scalar,
-};
-use rustc_hir::Mutability;
+use crate::interpret::{intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, Scalar};
use rustc_middle::mir;
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
mod error;
@@ -28,7 +24,7 @@ pub(crate) fn const_caller_location(
(file, line, col): (Symbol, u32, u32),
) -> ConstValue<'_> {
trace!("const_caller_location: {}:{}:{}", file, line, col);
- let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), false);
+ let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), CanAccessStatics::No);
let loc_place = ecx.alloc_caller_location(file, line, col);
if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
@@ -57,10 +53,12 @@ pub(crate) fn eval_to_valtree<'tcx>(
// FIXME Need to provide a span to `eval_to_valtree`
let ecx = mk_eval_cx(
- tcx, DUMMY_SP, param_env,
+ tcx,
+ DUMMY_SP,
+ param_env,
// It is absolutely crucial for soundness that
// we do not read from static items or other mutable memory.
- false,
+ CanAccessStatics::No,
);
let place = ecx.raw_const_to_mplace(const_alloc).unwrap();
debug!(?place);
@@ -75,17 +73,8 @@ pub(crate) fn eval_to_valtree<'tcx>(
let global_const_id = cid.display(tcx);
match err {
ValTreeCreationError::NodesOverflow => {
- let msg = format!(
- "maximum number of nodes exceeded in constant {}",
- &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();
+ let span = tcx.hir().span_if_local(did);
+ tcx.sess.emit_err(MaxNumNodesInConstErr { span, global_const_id });
Ok(None)
}
@@ -96,24 +85,24 @@ pub(crate) fn eval_to_valtree<'tcx>(
}
#[instrument(skip(tcx), level = "debug")]
-pub(crate) fn try_destructure_mir_constant<'tcx>(
+pub(crate) fn try_destructure_mir_constant_for_diagnostics<'tcx>(
tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- val: mir::ConstantKind<'tcx>,
-) -> InterpResult<'tcx, mir::DestructuredConstant<'tcx>> {
- trace!("destructure_mir_constant: {:?}", val);
- let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
- let op = ecx.eval_mir_constant(&val, None, None)?;
+ val: ConstValue<'tcx>,
+ ty: Ty<'tcx>,
+) -> Option<mir::DestructuredConstant<'tcx>> {
+ let param_env = ty::ParamEnv::reveal_all();
+ let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
+ let op = ecx.const_val_to_op(val, ty, None).ok()?;
// We go to `usize` as we cannot allocate anything bigger anyway.
- let (field_count, variant, down) = match val.ty().kind() {
+ let (field_count, variant, down) = match ty.kind() {
ty::Array(_, len) => (len.eval_target_usize(tcx, param_env) as usize, None, op),
ty::Adt(def, _) if def.variants().is_empty() => {
- throw_ub!(Unreachable)
+ return None;
}
ty::Adt(def, _) => {
- let variant = ecx.read_discriminant(&op)?.1;
- let down = ecx.operand_downcast(&op, variant)?;
+ let variant = ecx.read_discriminant(&op).ok()?.1;
+ let down = ecx.operand_downcast(&op, variant).ok()?;
(def.variants()[variant].fields.len(), Some(variant), down)
}
ty::Tuple(substs) => (substs.len(), None, op),
@@ -122,47 +111,12 @@ pub(crate) fn try_destructure_mir_constant<'tcx>(
let fields_iter = (0..field_count)
.map(|i| {
- let field_op = ecx.operand_field(&down, i)?;
+ let field_op = ecx.operand_field(&down, i).ok()?;
let val = op_to_const(&ecx, &field_op);
- Ok(mir::ConstantKind::Val(val, field_op.layout.ty))
+ Some((val, field_op.layout.ty))
})
- .collect::<InterpResult<'tcx, Vec<_>>>()?;
+ .collect::<Option<Vec<_>>>()?;
let fields = tcx.arena.alloc_from_iter(fields_iter);
- Ok(mir::DestructuredConstant { variant, fields })
-}
-
-#[instrument(skip(tcx), level = "debug")]
-pub(crate) fn deref_mir_constant<'tcx>(
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- val: mir::ConstantKind<'tcx>,
-) -> mir::ConstantKind<'tcx> {
- let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
- let op = ecx.eval_mir_constant(&val, None, None).unwrap();
- let mplace = ecx.deref_operand(&op).unwrap();
- if let Some(alloc_id) = mplace.ptr.provenance {
- assert_eq!(
- tcx.global_alloc(alloc_id).unwrap_memory().0.0.mutability,
- Mutability::Not,
- "deref_mir_constant cannot be used with mutable allocations as \
- that could allow pattern matching to observe mutable statics",
- );
- }
-
- let ty = match mplace.meta {
- MemPlaceMeta::None => mplace.layout.ty,
- // In case of unsized types, figure out the real type behind.
- MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() {
- ty::Str => bug!("there's no sized equivalent of a `str`"),
- ty::Slice(elem_ty) => tcx.mk_array(*elem_ty, scalar.to_target_usize(&tcx).unwrap()),
- _ => bug!(
- "type {} should not have metadata, but had {:?}",
- mplace.layout.ty,
- mplace.meta
- ),
- },
- };
-
- mir::ConstantKind::Val(op_to_const(&ecx, &mplace.into()), ty)
+ Some(mir::DestructuredConstant { variant, fields })
}
diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
index b10f2e9f8..e574df276 100644
--- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs
+++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
@@ -1,6 +1,7 @@
use super::eval_queries::{mk_eval_cx, op_to_const};
use super::machine::CompileTimeEvalContext;
use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES};
+use crate::const_eval::CanAccessStatics;
use crate::interpret::{
intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta,
MemoryKind, PlaceTy, Scalar,
@@ -263,7 +264,11 @@ pub fn valtree_to_const_value<'tcx>(
// FIXME Does this need an example?
let (param_env, ty) = param_env_ty.into_parts();
- let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
+ let mut ecx: crate::interpret::InterpCx<
+ '_,
+ '_,
+ crate::const_eval::CompileTimeInterpreter<'_, '_>,
+ > = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
match ty.kind() {
ty::FnDef(..) => {
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index f8b7cc6d7..ca38cce71 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -1,6 +1,24 @@
+use rustc_errors::{
+ DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Handler,
+ IntoDiagnostic,
+};
use rustc_hir::ConstContext;
-use rustc_macros::Diagnostic;
+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_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)]
#[diag(const_eval_unstable_in_stable)]
@@ -92,7 +110,7 @@ pub(crate) struct TransientMutBorrowErrRaw {
#[diag(const_eval_max_num_nodes_in_const)]
pub(crate) struct MaxNumNodesInConstErr {
#[primary_span]
- pub span: Span,
+ pub span: Option<Span>,
pub global_const_id: String,
}
@@ -176,6 +194,14 @@ pub(crate) struct UnallowedInlineAsm {
}
#[derive(Diagnostic)]
+#[diag(const_eval_unsupported_untyped_pointer)]
+#[note]
+pub(crate) struct UnsupportedUntypedPointer {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
#[diag(const_eval_interior_mutable_data_refer, code = "E0492")]
pub(crate) struct InteriorMutableDataRefer {
#[primary_span]
@@ -194,3 +220,661 @@ pub(crate) struct InteriorMutabilityBorrow {
#[primary_span]
pub span: Span,
}
+
+#[derive(LintDiagnostic)]
+#[diag(const_eval_long_running)]
+#[note]
+pub struct LongRunning {
+ #[help]
+ pub item_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(const_eval_long_running)]
+pub struct LongRunningWarn {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ #[help]
+ 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,
+}
+
+// FIXME(fee1-dead) do not use stringly typed `ConstContext`
+
+#[derive(Diagnostic)]
+#[diag(const_eval_match_eq_non_const, code = "E0015")]
+#[note]
+pub struct NonConstMatchEq<'tcx> {
+ #[primary_span]
+ pub span: Span,
+ pub ty: Ty<'tcx>,
+ pub kind: ConstContext,
+}
+
+#[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>) {}
+}
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index 163e3f869..83a072d6f 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -4,7 +4,7 @@ use rustc_apfloat::ieee::{Double, Single};
use rustc_apfloat::{Float, FloatConvert};
use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar};
use rustc_middle::mir::CastKind;
-use rustc_middle::ty::adjustment::PointerCast;
+use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, FloatTy, Ty, TypeAndMut};
use rustc_target::abi::Integer;
@@ -14,6 +14,8 @@ use super::{
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> {
pub fn cast(
&mut self,
@@ -22,51 +24,52 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
cast_ty: Ty<'tcx>,
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx> {
- use rustc_middle::mir::CastKind::*;
// FIXME: In which cases should we trigger UB when the source is uninit?
match cast_kind {
- Pointer(PointerCast::Unsize) => {
+ CastKind::PointerCoercion(PointerCoercion::Unsize) => {
let cast_ty = self.layout_of(cast_ty)?;
self.unsize_into(src, cast_ty, dest)?;
}
- PointerExposeAddress => {
+ CastKind::PointerExposeAddress => {
let src = self.read_immediate(src)?;
let res = self.pointer_expose_address_cast(&src, cast_ty)?;
self.write_immediate(res, dest)?;
}
- PointerFromExposedAddress => {
+ CastKind::PointerFromExposedAddress => {
let src = self.read_immediate(src)?;
let res = self.pointer_from_exposed_address_cast(&src, cast_ty)?;
self.write_immediate(res, dest)?;
}
- IntToInt | IntToFloat => {
+ CastKind::IntToInt | CastKind::IntToFloat => {
let src = self.read_immediate(src)?;
let res = self.int_to_int_or_float(&src, cast_ty)?;
self.write_immediate(res, dest)?;
}
- FloatToFloat | FloatToInt => {
+ CastKind::FloatToFloat | CastKind::FloatToInt => {
let src = self.read_immediate(src)?;
let res = self.float_to_float_or_int(&src, cast_ty)?;
self.write_immediate(res, dest)?;
}
- FnPtrToPtr | PtrToPtr => {
+ CastKind::FnPtrToPtr | CastKind::PtrToPtr => {
let src = self.read_immediate(&src)?;
let res = self.ptr_to_ptr(&src, cast_ty)?;
self.write_immediate(res, dest)?;
}
- Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer) => {
+ CastKind::PointerCoercion(
+ PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer,
+ ) => {
// These are NOPs, but can be wide pointers.
let v = self.read_immediate(src)?;
self.write_immediate(*v, dest)?;
}
- Pointer(PointerCast::ReifyFnPointer) => {
+ CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => {
// All reifications must be monomorphic, bail out otherwise.
ensure_monomorphic_enough(*self.tcx, src.layout.ty)?;
@@ -88,7 +91,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
- Pointer(PointerCast::UnsafeFnPointer) => {
+ CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer) => {
let src = self.read_immediate(src)?;
match cast_ty.kind() {
ty::FnPtr(_) => {
@@ -99,7 +102,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
- Pointer(PointerCast::ClosureFnPointer(_)) => {
+ CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)) => {
// All reifications must be monomorphic, bail out otherwise.
ensure_monomorphic_enough(*self.tcx, src.layout.ty)?;
@@ -120,7 +123,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
- DynStar => {
+ CastKind::DynStar => {
if let ty::Dynamic(data, _, ty::DynStar) = cast_ty.kind() {
// Initial cast from sized to dyn trait
let vtable = self.get_vtable_ptr(src.layout.ty, data.principal())?;
@@ -134,16 +137,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
- Transmute => {
+ CastKind::Transmute => {
assert!(src.layout.is_sized());
assert!(dest.layout.is_sized());
if src.layout.size != dest.layout.size {
- throw_ub_format!(
- "transmuting from {}-byte type to {}-byte type: `{}` -> `{}`",
- src.layout.size.bytes(),
- dest.layout.size.bytes(),
- src.layout.ty,
- dest.layout.ty,
+ let src_bytes = src.layout.size.bytes();
+ let dest_bytes = dest.layout.size.bytes();
+ let src_ty = format!("{}", src.layout.ty);
+ let dest_ty = format!("{}", dest.layout.ty);
+ throw_ub_custom!(
+ fluent::const_eval_invalid_transmute,
+ src_bytes = src_bytes,
+ dest_bytes = dest_bytes,
+ src = src_ty,
+ dest = dest_ty,
);
}
@@ -363,7 +370,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let old_vptr = old_vptr.to_pointer(self)?;
let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?;
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())?;
self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 7e9457800..36606ff69 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -1,13 +1,13 @@
use std::cell::Cell;
-use std::fmt;
-use std::mem;
+use std::{fmt, mem};
use either::{Either, Left, Right};
+use hir::CRATE_HIR_ID;
use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
use rustc_index::IndexVec;
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::ty::layout::{
self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers,
@@ -24,6 +24,8 @@ use super::{
MemPlaceMeta, Memory, MemoryKind, Operand, Place, PlaceTy, PointerArithmetic, Provenance,
Scalar, StackPopJump,
};
+use crate::errors::{self, ErroneousConstUsed};
+use crate::fluent_generated as fluent;
use crate::util;
pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
@@ -246,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> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
ty::tls::with(|tcx| {
@@ -263,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> {
#[inline]
fn data_layout(&self) -> &TargetDataLayout {
@@ -406,6 +424,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
#[inline(always)]
+ /// Find the first stack frame that is within the current crate, if any, otherwise return the crate's HirId
+ pub fn best_lint_scope(&self) -> hir::HirId {
+ self.stack()
+ .iter()
+ .find_map(|frame| frame.body.source.def_id().as_local())
+ .map_or(CRATE_HIR_ID, |def_id| self.tcx.hir().local_def_id_to_hir_id(def_id))
+ }
+
+ #[inline(always)]
pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>] {
M::stack(self)
}
@@ -497,7 +524,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
.try_subst_mir_and_normalize_erasing_regions(
*self.tcx,
self.param_env,
- ty::EarlyBinder(value),
+ ty::EarlyBinder::bind(value),
)
.map_err(|_| err_inval!(TooGeneric))
}
@@ -610,7 +637,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Check if this brought us over the size limit.
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)))
}
@@ -628,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 = Size::from_bytes(size);
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)))
}
@@ -736,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::Continue => Right(self.frame_mut().body.span),
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 => {
self.frame_mut().loc = Right(self.frame_mut().body.span);
@@ -775,7 +802,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
);
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.
@@ -863,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.
let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val);
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(())
}
@@ -906,7 +933,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
ErrorHandled::Reported(err) => {
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.
- self.tcx.sess.span_note_without_error(span, "erroneous constant used");
+ self.tcx.sess.emit_note(ErroneousConstUsed { span });
}
err_inval!(AlreadyReported(err))
}
diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs
index c2b82ba9b..7b11ad330 100644
--- a/compiler/rustc_const_eval/src/interpret/intern.rs
+++ b/compiler/rustc_const_eval/src/interpret/intern.rs
@@ -28,6 +28,7 @@ use super::{
ValueVisitor,
};
use crate::const_eval;
+use crate::errors::{DanglingPtrInFinal, UnsupportedUntypedPointer};
pub trait CompileTimeMachine<'mir, 'tcx, T> = Machine<
'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)]
pub enum InternKind {
/// The `mutability` of the static, ignoring the type which may have interior mutability.
Static(hir::Mutability),
+ /// A `const` item
Constant,
Promoted,
}
@@ -388,8 +391,7 @@ pub fn intern_const_alloc_recursive<
ecx.tcx.sess.delay_span_bug(
ecx.tcx.span,
format!(
- "error during interning should later cause validation failure: {}",
- error
+ "error during interning should later cause validation failure: {error:?}"
),
);
}
@@ -425,14 +427,16 @@ pub fn intern_const_alloc_recursive<
// immutability is so important.
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 => {
- // 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 _;`.
- ecx.tcx
- .sess
- .span_err(ecx.tcx.span, "untyped pointers are not allowed in constant");
+ ecx.tcx.sess.emit_err(UnsupportedUntypedPointer { span: ecx.tcx.span });
// For better errors later, mark the allocation as immutable.
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) {
// Codegen does not like dangling pointers, and generally `tcx` assumes that
// all allocations referenced anywhere actually exist. So, make sure we error here.
- let reported = ecx
- .tcx
- .sess
- .span_err(ecx.tcx.span, "encountered dangling pointer in final constant");
+ let reported = ecx.tcx.sess.emit_err(DanglingPtrInFinal { span: ecx.tcx.span });
return Err(reported);
} 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
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index a77c699c2..ed64a7655 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -22,6 +22,8 @@ use super::{
Pointer,
};
+use crate::fluent_generated as fluent;
+
mod caller_location;
fn numeric_intrinsic<Prov>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Prov> {
@@ -70,12 +72,12 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>(
}
sym::pref_align_of => {
// Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough.
- let layout = tcx.layout_of(param_env.and(tp_ty)).map_err(|e| err_inval!(Layout(e)))?;
+ let layout = tcx.layout_of(param_env.and(tp_ty)).map_err(|e| err_inval!(Layout(*e)))?;
ConstValue::from_target_usize(layout.align.pref.bytes(), &tcx)
}
sym::type_id => {
ensure_monomorphic_enough(tcx, tp_ty)?;
- ConstValue::from_u64(tcx.type_id_hash(tp_ty).as_u64())
+ ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128())
}
sym::variant_count => match tp_ty.kind() {
// Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough.
@@ -167,8 +169,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let ty = match intrinsic_name {
sym::pref_align_of | sym::variant_count => self.tcx.types.usize,
sym::needs_drop => self.tcx.types.bool,
- sym::type_id => self.tcx.types.u64,
- sym::type_name => self.tcx.mk_static_str(),
+ sym::type_id => self.tcx.types.u128,
+ sym::type_name => Ty::new_static_str(self.tcx.tcx),
_ => bug!(),
};
let val = self.ctfe_query(None, |tcx| {
@@ -198,15 +200,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
ty
),
};
- let (nonzero, intrinsic_name) = match intrinsic_name {
+ let (nonzero, actual_intrinsic_name) = match intrinsic_name {
sym::cttz_nonzero => (true, sym::cttz),
sym::ctlz_nonzero => (true, sym::ctlz),
other => (false, other),
};
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)?;
}
sym::saturating_add | sym::saturating_sub => {
@@ -229,37 +234,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let r = self.read_immediate(&args[1])?;
self.exact_div(&l, &r, dest)?;
}
- sym::unchecked_shl
- | sym::unchecked_shr
- | sym::unchecked_add
- | sym::unchecked_sub
- | sym::unchecked_mul
- | sym::unchecked_div
- | sym::unchecked_rem => {
- let l = self.read_immediate(&args[0])?;
- let r = self.read_immediate(&args[1])?;
- let bin_op = match intrinsic_name {
- sym::unchecked_shl => BinOp::Shl,
- sym::unchecked_shr => BinOp::Shr,
- sym::unchecked_add => BinOp::Add,
- sym::unchecked_sub => BinOp::Sub,
- sym::unchecked_mul => BinOp::Mul,
- sym::unchecked_div => BinOp::Div,
- sym::unchecked_rem => BinOp::Rem,
- _ => bug!(),
- };
- let (val, overflowed, _ty) = self.overflowing_binary_op(bin_op, &l, &r)?;
- if overflowed {
- let layout = self.layout_of(substs.type_at(0))?;
- let r_val = r.to_scalar().to_bits(layout.size)?;
- if let sym::unchecked_shl | sym::unchecked_shr = intrinsic_name {
- throw_ub_format!("overflowing shift by {} in `{}`", r_val, intrinsic_name);
- } else {
- throw_ub_format!("overflow executing `{}`", intrinsic_name);
- }
- }
- self.write_scalar(val, dest)?;
- }
sym::rotate_left | sym::rotate_right => {
// rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW))
// rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW))
@@ -314,17 +288,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
(Err(_), _) | (_, Err(_)) => {
// 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.
- throw_ub_format!(
- "`{}` called on pointers into different allocations",
- intrinsic_name
+ throw_ub_custom!(
+ fluent::const_eval_different_allocations,
+ name = intrinsic_name,
);
}
(Ok((a_alloc_id, a_offset, _)), Ok((b_alloc_id, b_offset, _))) => {
// Found allocation for both. They must be into the same allocation.
if a_alloc_id != b_alloc_id {
- throw_ub_format!(
- "`{}` called on pointers into different allocations",
- intrinsic_name
+ throw_ub_custom!(
+ fluent::const_eval_different_allocations,
+ name = intrinsic_name,
);
}
// Use these offsets for distance calculation.
@@ -344,11 +318,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
if overflowed {
// a < b
if intrinsic_name == sym::ptr_offset_from_unsigned {
- throw_ub_format!(
- "`{}` called when first pointer has smaller offset than second: {} < {}",
- intrinsic_name,
- a_offset,
- b_offset,
+ throw_ub_custom!(
+ fluent::const_eval_unsigned_offset_from_overflow,
+ a_offset = a_offset,
+ b_offset = b_offset,
);
}
// The signed form of the intrinsic allows this. If we interpret the
@@ -356,9 +329,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// seems *positive*, they were more than isize::MAX apart.
let dist = val.to_target_isize(self)?;
if dist >= 0 {
- throw_ub_format!(
- "`{}` called when first pointer is too far before second",
- intrinsic_name
+ throw_ub_custom!(
+ fluent::const_eval_offset_from_underflow,
+ name = intrinsic_name,
);
}
dist
@@ -368,9 +341,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
// because they were more than isize::MAX apart.
if dist < 0 {
- throw_ub_format!(
- "`{}` called when first pointer is too far ahead of second",
- intrinsic_name
+ throw_ub_custom!(
+ fluent::const_eval_offset_from_overflow,
+ name = intrinsic_name,
);
}
dist
@@ -513,7 +486,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let op = self.eval_operand(op, None)?;
let cond = self.read_scalar(&op)?.to_bool()?;
if !cond {
- throw_ub_format!("`assume` called with `false`");
+ throw_ub_custom!(fluent::const_eval_assume_false);
}
Ok(())
}
@@ -542,7 +515,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)?;
assert!(!overflow); // All overflow is UB, so this should never return on overflow.
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.
self.binop_ignore_overflow(BinOp::Div, &a, &b, dest)
@@ -638,9 +615,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),
// but no actual allocation can be big enough for the difference to be noticeable.
let size = size.checked_mul(count, self).ok_or_else(|| {
- err_ub_format!(
- "overflow computing total size of `{}`",
- if nonoverlapping { "copy_nonoverlapping" } else { "copy" }
+ err_ub_custom!(
+ fluent::const_eval_size_overflow,
+ name = if nonoverlapping { "copy_nonoverlapping" } else { "copy" }
)
})?;
@@ -664,10 +641,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),
// but no actual allocation can be big enough for the difference to be noticeable.
- let len = layout
- .size
- .checked_mul(count, self)
- .ok_or_else(|| err_ub_format!("overflow computing total size of `write_bytes`"))?;
+ let len = layout.size.checked_mul(count, self).ok_or_else(|| {
+ err_ub_custom!(fluent::const_eval_size_overflow, name = "write_bytes")
+ })?;
let bytes = std::iter::repeat(byte).take(len.bytes_usize());
self.write_bytes_ptr(dst, bytes)
@@ -691,7 +667,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
return Ok(&[]);
};
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()
};
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index d5b6a581a..1125d8d1f 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -19,6 +19,7 @@ use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
use rustc_target::abi::{Align, HasDataLayout, Size};
use crate::const_eval::CheckAlignment;
+use crate::fluent_generated as fluent;
use super::{
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,
kind: MemoryKind<M::MemoryKind>,
) -> 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)
}
@@ -242,9 +247,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
) -> InterpResult<'tcx, Pointer<M::Provenance>> {
let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr)?;
if offset.bytes() != 0 {
- throw_ub_format!(
- "reallocating {:?} which does not point to the beginning of an object",
- ptr
+ throw_ub_custom!(
+ fluent::const_eval_realloc_or_alloc_with_offset,
+ 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:?}");
if offset.bytes() != 0 {
- throw_ub_format!(
- "deallocating {:?} which does not point to the beginning of an object",
- ptr
+ throw_ub_custom!(
+ fluent::const_eval_realloc_or_alloc_with_offset,
+ 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
return Err(match self.tcx.try_get_global_alloc(alloc_id) {
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(..)) => {
- 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(..)) => {
- 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)),
}
@@ -304,21 +323,25 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
};
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 {
- throw_ub_format!(
- "deallocating {alloc_id:?}, which is {alloc_kind} memory, using {kind} deallocation operation"
+ throw_ub_custom!(
+ 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 size != alloc.size() || align != alloc.align {
- throw_ub_format!(
- "incorrect layout on deallocation: {alloc_id:?} has size {} and alignment {}, but gave size {} and alignment {}",
- alloc.size().bytes(),
- alloc.align.bytes(),
- size.bytes(),
- align.bytes(),
+ throw_ub_custom!(
+ fluent::const_eval_dealloc_incorrect_layout,
+ alloc = alloc_id,
+ size = alloc.size().bytes(),
+ align = alloc.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)
|| (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);
}
}
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index e30af1655..5f89d652f 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -106,7 +106,7 @@ impl<Prov: Provenance> std::fmt::Display for ImmTy<'_, Prov> {
// Just print the ptr value. `pretty_print_const_scalar_ptr` would also try to
// print what is points to, which would fail since it has no access to the local
// memory.
- cx.pretty_print_const_pointer(ptr, ty, true)
+ cx.pretty_print_const_pointer(ptr, ty)
}
}
}
@@ -633,7 +633,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
- pub(super) fn const_val_to_op(
+ pub(crate) fn const_val_to_op(
&self,
val_val: ConstValue<'tcx>,
ty: Ty<'tcx>,
diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs
index 7186148da..e04764636 100644
--- a/compiler/rustc_const_eval/src/interpret/operator.rs
+++ b/compiler/rustc_const_eval/src/interpret/operator.rs
@@ -3,10 +3,13 @@ use rustc_middle::mir;
use rustc_middle::mir::interpret::{InterpResult, Scalar};
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, FloatTy, Ty};
+use rustc_span::symbol::sym;
use rustc_target::abi::Abi;
use super::{ImmTy, Immediate, InterpCx, Machine, PlaceTy};
+use crate::fluent_generated as fluent;
+
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// Applies the binary operation `op` to the two operands and writes a tuple of the result
/// and a boolean signifying the potential overflow to the destination.
@@ -19,7 +22,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
) -> InterpResult<'tcx> {
let (val, overflowed, ty) = self.overflowing_binary_op(op, &left, &right)?;
debug_assert_eq!(
- self.tcx.mk_tup(&[ty, self.tcx.types.bool]),
+ Ty::new_tup(self.tcx.tcx, &[ty, self.tcx.types.bool]),
dest.layout.ty,
"type mismatch for result of {:?}",
op,
@@ -139,8 +142,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
) -> InterpResult<'tcx, (Scalar<M::Provenance>, bool, Ty<'tcx>)> {
use rustc_middle::mir::BinOp::*;
+ let throw_ub_on_overflow = match bin_op {
+ AddUnchecked => Some(sym::unchecked_add),
+ SubUnchecked => Some(sym::unchecked_sub),
+ MulUnchecked => Some(sym::unchecked_mul),
+ ShlUnchecked => Some(sym::unchecked_shl),
+ ShrUnchecked => Some(sym::unchecked_shr),
+ _ => None,
+ };
+
// Shift ops can have an RHS with a different numeric type.
- if bin_op == Shl || bin_op == Shr {
+ if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) {
let size = u128::from(left_layout.size.bits());
// Even if `r` is signed, we treat it as if it was unsigned (i.e., we use its
// zero-extended form). This matches the codegen backend:
@@ -155,6 +167,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// integers are maximally 128bits wide, so negative shifts *always* overflow and we have
// consistent results for the same value represented at different bit widths.
assert!(size <= 128);
+ let original_r = r;
let overflow = r >= size;
// The shift offset is implicitly masked to the type size, to make sure this operation
// is always defined. This is the one MIR operator that does *not* directly map to a
@@ -166,19 +179,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let result = if left_layout.abi.is_signed() {
let l = self.sign_extend(l, left_layout) as i128;
let result = match bin_op {
- Shl => l.checked_shl(r).unwrap(),
- Shr => l.checked_shr(r).unwrap(),
+ Shl | ShlUnchecked => l.checked_shl(r).unwrap(),
+ Shr | ShrUnchecked => l.checked_shr(r).unwrap(),
_ => bug!(),
};
result as u128
} else {
match bin_op {
- Shl => l.checked_shl(r).unwrap(),
- Shr => l.checked_shr(r).unwrap(),
+ Shl | ShlUnchecked => l.checked_shl(r).unwrap(),
+ Shr | ShrUnchecked => l.checked_shr(r).unwrap(),
_ => bug!(),
}
};
let truncated = self.truncate(result, left_layout);
+
+ if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
+ throw_ub_custom!(
+ fluent::const_eval_overflow_shift,
+ val = original_r,
+ name = intrinsic_name
+ );
+ }
+
return Ok((Scalar::from_uint(truncated, left_layout.size), overflow, left_layout.ty));
}
@@ -216,9 +238,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Rem if r == 0 => throw_ub!(RemainderByZero),
Div => Some(i128::overflowing_div),
Rem => Some(i128::overflowing_rem),
- Add => Some(i128::overflowing_add),
- Sub => Some(i128::overflowing_sub),
- Mul => Some(i128::overflowing_mul),
+ Add | AddUnchecked => Some(i128::overflowing_add),
+ Sub | SubUnchecked => Some(i128::overflowing_sub),
+ Mul | MulUnchecked => Some(i128::overflowing_mul),
_ => None,
};
if let Some(op) = op {
@@ -242,11 +264,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// If that truncation loses any information, we have an overflow.
let result = result as u128;
let truncated = self.truncate(result, left_layout);
- return Ok((
- Scalar::from_uint(truncated, size),
- oflo || self.sign_extend(truncated, left_layout) != result,
- left_layout.ty,
- ));
+ let overflow = oflo || self.sign_extend(truncated, left_layout) != result;
+ if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
+ throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
+ }
+ return Ok((Scalar::from_uint(truncated, size), overflow, left_layout.ty));
}
}
@@ -263,12 +285,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
BitAnd => (Scalar::from_uint(l & r, size), left_layout.ty),
BitXor => (Scalar::from_uint(l ^ r, size), left_layout.ty),
- Add | Sub | Mul | Rem | Div => {
+ Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Rem | Div => {
assert!(!left_layout.abi.is_signed());
let op: fn(u128, u128) -> (u128, bool) = match bin_op {
- Add => u128::overflowing_add,
- Sub => u128::overflowing_sub,
- Mul => u128::overflowing_mul,
+ Add | AddUnchecked => u128::overflowing_add,
+ Sub | SubUnchecked => u128::overflowing_sub,
+ Mul | MulUnchecked => u128::overflowing_mul,
Div if r == 0 => throw_ub!(DivisionByZero),
Rem if r == 0 => throw_ub!(RemainderByZero),
Div => u128::overflowing_div,
@@ -279,11 +301,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Truncate to target type.
// If that truncation loses any information, we have an overflow.
let truncated = self.truncate(result, left_layout);
- return Ok((
- Scalar::from_uint(truncated, size),
- oflo || truncated != result,
- left_layout.ty,
- ));
+ let overflow = oflo || truncated != result;
+ if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
+ throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
+ }
+ return Ok((Scalar::from_uint(truncated, size), overflow, left_layout.ty));
}
_ => span_bug!(
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 2a31a59ad..ca1106384 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -9,6 +9,7 @@ use rustc_index::IndexSlice;
use rustc_middle::mir;
use rustc_middle::ty;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
+use rustc_middle::ty::Ty;
use rustc_target::abi::{self, Abi, Align, FieldIdx, HasDataLayout, Size, FIRST_VARIANT};
use super::{
@@ -395,7 +396,7 @@ where
// (Transmuting is okay since this is an in-memory place. We also double-check the size
// stays the same.)
let (len, e_ty) = mplace.layout.ty.simd_size_and_type(*self.tcx);
- let array = self.tcx.mk_array(e_ty, len);
+ let array = Ty::new_array(self.tcx.tcx, e_ty, len);
let layout = self.layout_of(array)?;
assert_eq!(layout.size, mplace.layout.size);
Ok((MPlaceTy { layout, ..*mplace }, len))
@@ -699,8 +700,13 @@ where
assert_eq!(src.layout.size, dest.layout.size);
}
+ // Setting `nonoverlapping` here only has an effect when we don't hit the fast-path above,
+ // but that should at least match what LLVM does where `memcpy` is also only used when the
+ // type does not have Scalar/ScalarPair layout.
+ // (Or as the `Assign` docs put it, assignments "not producing primitives" must be
+ // non-overlapping.)
self.mem_copy(
- src.ptr, src.align, dest.ptr, dest.align, dest_size, /*nonoverlapping*/ false,
+ src.ptr, src.align, dest.ptr, dest.align, dest_size, /*nonoverlapping*/ true,
)
}
@@ -775,7 +781,8 @@ where
let meta = Scalar::from_target_usize(u64::try_from(str.len()).unwrap(), self);
let mplace = MemPlace { ptr: ptr.into(), meta: MemPlaceMeta::Meta(meta) };
- let ty = self.tcx.mk_ref(
+ let ty = Ty::new_ref(
+ self.tcx.tcx,
self.tcx.lifetimes.re_static,
ty::TypeAndMut { ty: self.tcx.types.str_, mutbl },
);
diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs
index 91da930db..d7d31fe18 100644
--- a/compiler/rustc_const_eval/src/interpret/projection.rs
+++ b/compiler/rustc_const_eval/src/interpret/projection.rs
@@ -12,6 +12,7 @@ use either::{Left, Right};
use rustc_middle::mir;
use rustc_middle::ty;
use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::ty::Ty;
use rustc_target::abi::{self, Abi, VariantIdx};
use super::{
@@ -317,7 +318,9 @@ where
let (meta, ty) = match base.layout.ty.kind() {
// It is not nice to match on the type, but that seems to be the only way to
// implement this.
- ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(*inner, inner_len)),
+ ty::Array(inner, _) => {
+ (MemPlaceMeta::None, Ty::new_array(self.tcx.tcx, *inner, inner_len))
+ }
ty::Slice(..) => {
let len = Scalar::from_target_usize(inner_len, self);
(MemPlaceMeta::Meta(len), base.layout.ty)
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 1e60a1e72..619da8abb 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -9,27 +9,7 @@ use rustc_middle::mir::interpret::{InterpResult, Scalar};
use rustc_middle::ty::layout::LayoutOf;
use super::{ImmTy, InterpCx, Machine};
-
-/// Classify whether an operator is "left-homogeneous", i.e., the LHS has the
-/// same type as the result.
-#[inline]
-fn binop_left_homogeneous(op: mir::BinOp) -> bool {
- use rustc_middle::mir::BinOp::*;
- match op {
- Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Offset | Shl | Shr => true,
- Eq | Ne | Lt | Le | Gt | Ge => false,
- }
-}
-/// Classify whether an operator is "right-homogeneous", i.e., the RHS has the
-/// same type as the LHS.
-#[inline]
-fn binop_right_homogeneous(op: mir::BinOp) -> bool {
- use rustc_middle::mir::BinOp::*;
- match op {
- Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true,
- Offset | Shl | Shr => false,
- }
-}
+use crate::util;
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// Returns `true` as long as there are more things to do.
@@ -179,9 +159,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
BinaryOp(bin_op, box (ref left, ref right)) => {
- let layout = binop_left_homogeneous(bin_op).then_some(dest.layout);
+ let layout = util::binop_left_homogeneous(bin_op).then_some(dest.layout);
let left = self.read_immediate(&self.eval_operand(left, layout)?)?;
- let layout = binop_right_homogeneous(bin_op).then_some(left.layout);
+ let layout = util::binop_right_homogeneous(bin_op).then_some(left.layout);
let right = self.read_immediate(&self.eval_operand(right, layout)?)?;
self.binop_ignore_overflow(bin_op, &left, &right, &dest)?;
}
@@ -189,7 +169,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
CheckedBinaryOp(bin_op, box (ref left, ref right)) => {
// Due to the extra boolean in the result, we can never reuse the `dest.layout`.
let left = self.read_immediate(&self.eval_operand(left, None)?)?;
- let layout = binop_right_homogeneous(bin_op).then_some(left.layout);
+ let layout = util::binop_right_homogeneous(bin_op).then_some(left.layout);
let right = self.read_immediate(&self.eval_operand(right, layout)?)?;
self.binop_with_overflow(bin_op, &left, &right, &dest)?;
}
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index 586e8f063..15823a597 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -15,6 +15,7 @@ use super::{
FnVal, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemoryKind, OpTy, Operand,
PlaceTy, Scalar, StackPopCleanup,
};
+use crate::fluent_generated as fluent;
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
pub(super) fn eval_terminator(
@@ -61,7 +62,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
destination,
target,
unwind,
- from_hir_call: _,
+ call_source: _,
fn_span: _,
} => {
let old_stack = self.frame_idx();
@@ -172,7 +173,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
InlineAsm { template, ref operands, options, destination, .. } => {
M::eval_inline_asm(self, template, operands, options)?;
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(
destination
@@ -288,15 +289,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
return Ok(());
}
// Find next caller arg.
- let (caller_arg, caller_abi) = caller_args.next().ok_or_else(|| {
- err_ub_format!("calling a function with fewer arguments than it requires")
- })?;
+ let Some((caller_arg, caller_abi)) = caller_args.next() else {
+ throw_ub_custom!(fluent::const_eval_not_enough_caller_args);
+ };
// Now, check
if !Self::check_argument_compat(caller_abi, callee_abi) {
- throw_ub_format!(
- "calling a function with argument of type {:?} passing data of type {:?}",
- callee_arg.layout.ty,
- caller_arg.layout.ty
+ let callee_ty = format!("{}", callee_arg.layout.ty);
+ let caller_ty = format!("{}", caller_arg.layout.ty);
+ throw_ub_custom!(
+ fluent::const_eval_incompatible_types,
+ callee_ty = callee_ty,
+ caller_ty = caller_ty,
)
}
// 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 caller_fn_abi.conv != callee_fn_abi.conv {
- throw_ub_format!(
- "calling a function with calling convention {:?} using calling convention {:?}",
- callee_fn_abi.conv,
- caller_fn_abi.conv
+ throw_ub_custom!(
+ fluent::const_eval_incompatible_calling_conventions,
+ callee_conv = format!("{:?}", callee_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"
);
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!
if !Self::check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret) {
- throw_ub_format!(
- "calling a function with return type {:?} passing \
- return place of type {:?}",
- callee_fn_abi.ret.layout.ty,
- caller_fn_abi.ret.layout.ty,
+ let callee_ty = format!("{}", callee_fn_abi.ret.layout.ty);
+ let caller_ty = format!("{}", caller_fn_abi.ret.layout.ty);
+ throw_ub_custom!(
+ fluent::const_eval_incompatible_return_types,
+ 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 (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
if dyn_trait != data.principal() {
- throw_ub_format!(
- "`dyn*` call on a pointer whose vtable does not match its type"
- );
+ throw_ub_custom!(fluent::const_eval_dyn_star_call_vtable_mismatch);
}
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 (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
if dyn_trait != data.principal() {
- throw_ub_format!(
- "`dyn` call on a pointer whose vtable does not match its type"
- );
+ throw_ub_custom!(fluent::const_eval_dyn_call_vtable_mismatch);
}
// 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
// compare them to ensure everything fits.
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:#?}");
if cfg!(debug_assertions) {
@@ -649,7 +650,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Adjust receiver argument. Layout can be any (thin) ptr.
args[0] = ImmTy::from_immediate(
Scalar::from_maybe_pointer(adjusted_receiver, self).into(),
- self.layout_of(self.tcx.mk_mut_ptr(dyn_ty))?,
+ self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, dyn_ty))?,
)
.into();
trace!("Patched receiver operand to {:#?}", args[0]);
@@ -702,7 +703,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let arg = ImmTy::from_immediate(
place.to_ref(self),
- self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?,
+ self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, place.layout.ty))?,
);
let ret = MPlaceTy::fake_alloc_zst(self.layout_of(self.tcx.types.unit)?);
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 01b772899..21c655988 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -4,7 +4,7 @@
//! That's useful because it means other passes (e.g. promotion) can rely on `const`s
//! to be const-safe.
-use std::fmt::{Display, Write};
+use std::fmt::Write;
use std::num::NonZeroUsize;
use either::{Left, Right};
@@ -12,7 +12,10 @@ use either::{Left, Right};
use rustc_ast::Mutability;
use rustc_data_structures::fx::FxHashSet;
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::layout::{LayoutOf, TyAndLayout};
use rustc_span::symbol::{sym, Symbol};
@@ -30,14 +33,7 @@ use super::{
};
macro_rules! throw_validation_failure {
- ($where:expr, { $( $what_fmt:tt )* } $( expected { $( $expected_fmt:tt )* } )?) => {{
- 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();
- )?
+ ($where:expr, $kind: expr) => {{
let where_ = &$where;
let path = if !where_.is_empty() {
let mut path = String::new();
@@ -46,7 +42,8 @@ macro_rules! throw_validation_failure {
} else {
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 {
($e:expr, $where:expr,
- $( $( $p:pat_param )|+ => { $( $what_fmt:tt )* } $( expected { $( $expected_fmt:tt )* } )? ),+ $(,)?
+ $( $( $p:pat_param )|+ => $kind: expr ),+ $(,)?
) => {{
match $e {
Ok(x) => x,
// 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.
- Err(e) => match e.kind() {
+ Err(e) => match e.into_parts() {
$(
- InterpError::UndefinedBehavior($($p)|+) =>
+ (InterpError::UndefinedBehavior($($p)|+), _) =>
throw_validation_failure!(
$where,
- { $( $what_fmt )* } $( expected { $( $expected_fmt )* } )?
+ $kind
)
),+,
#[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
fn write_path(out: &mut String, path: &[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>> {
/// 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
@@ -311,19 +289,19 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
fn read_immediate(
&self,
op: &OpTy<'tcx, M::Provenance>,
- expected: impl Display,
+ expected: ExpectedKind,
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
Ok(try_validation!(
self.ecx.read_immediate(op),
self.path,
- InvalidUninitBytes(None) => { "uninitialized memory" } expected { "{expected}" }
+ InvalidUninitBytes(None) => Uninit { expected }
))
}
fn read_scalar(
&self,
op: &OpTy<'tcx, M::Provenance>,
- expected: impl Display,
+ expected: ExpectedKind,
) -> InterpResult<'tcx, Scalar<M::Provenance>> {
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.path,
DanglingIntPointer(..) |
- InvalidVTablePointer(..) =>
- { "{vtable}" } expected { "a vtable pointer" },
+ InvalidVTablePointer(..) => InvalidVTablePtr { value: format!("{vtable}") }
);
// 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(
&mut self,
value: &OpTy<'tcx, M::Provenance>,
- kind: &str,
+ ptr_kind: PointerKind,
) -> InterpResult<'tcx> {
- let place =
- self.ecx.ref_to_mplace(&self.read_immediate(value, format_args!("a {kind}"))?)?;
+ let place = self.ecx.ref_to_mplace(&self.read_immediate(value, ptr_kind.into())?)?;
// Handle wide pointers.
// Check metadata early, for better diagnostics
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!(
self.ecx.size_and_align_of_mplace(&place),
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
// 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
),
self.path,
- AlignmentCheckFailed { required, has } =>
- {
- "an unaligned {kind} (required {} byte alignment but found {})",
- required.bytes(),
- has.bytes(),
- },
- DanglingIntPointer(0, _) =>
- { "a null {kind}" },
- DanglingIntPointer(i, _) =>
- {
- "a dangling {kind} ({pointer} has no provenance)",
- pointer = Pointer::<Option<AllocId>>::from_addr_invalid(*i),
- },
- PointerOutOfBounds { .. } =>
- { "a dangling {kind} (going beyond the bounds of its allocation)" },
+ AlignmentCheckFailed { required, has } => UnalignedPtr {
+ ptr_kind,
+ required_bytes: required.bytes(),
+ found_bytes: has.bytes()
+ },
+ DanglingIntPointer(0, _) => NullPtr { ptr_kind },
+ DanglingIntPointer(i, _) => DanglingPtrNoProvenance {
+ ptr_kind,
+ // FIXME this says "null pointer" when null but we need translate
+ pointer: format!("{}", Pointer::<Option<AllocId>>::from_addr_invalid(i))
+ },
+ PointerOutOfBounds { .. } => DanglingPtrOutOfBounds {
+ ptr_kind
+ },
// This cannot happen during const-eval (because interning already detects
// dangling pointers), but it can happen in Miri.
- PointerUseAfterFree(..) =>
- { "a dangling {kind} (use-after-free)" },
+ PointerUseAfterFree(..) => DanglingPtrUseAfterFree {
+ ptr_kind,
+ },
);
// Do not allow pointers to uninhabited types.
if place.layout.abi.is_uninhabited() {
- throw_validation_failure!(self.path,
- { "a {kind} pointing to uninhabited type {}", place.layout.ty }
- )
+ let ty = place.layout.ty;
+ throw_validation_failure!(self.path, PtrToUninhabited { ptr_kind, ty })
}
// Recursive checking
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 reachable when the const just referenced the static,
// but never read it (so we never entered `before_access_global`).
- throw_validation_failure!(self.path,
- { "a {} pointing to a static variable in a constant", kind }
- );
+ throw_validation_failure!(self.path, PtrToStatic { ptr_kind });
}
// 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
@@ -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
// out of a `static`, then that pointer might point to mutable memory,
// and we would catch that here.
- throw_validation_failure!(self.path,
- { "a {} pointing to mutable memory in a constant", kind }
- );
+ throw_validation_failure!(self.path, PtrToMut { ptr_kind });
}
}
// 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;
match ty.kind() {
ty::Bool => {
- let value = self.read_scalar(value, "a boolean")?;
+ let value = self.read_scalar(value, ExpectedKind::Bool)?;
try_validation!(
value.to_bool(),
self.path,
- InvalidBool(..) =>
- { "{:x}", value } expected { "a boolean" },
+ InvalidBool(..) => ValidationErrorKind::InvalidBool {
+ value: format!("{value:x}"),
+ }
);
Ok(true)
}
ty::Char => {
- let value = self.read_scalar(value, "a unicode scalar value")?;
+ let value = self.read_scalar(value, ExpectedKind::Char)?;
try_validation!(
value.to_char(),
self.path,
- InvalidChar(..) =>
- { "{:x}", value } expected { "a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`)" },
+ InvalidChar(..) => ValidationErrorKind::InvalidChar {
+ value: format!("{value:x}"),
+ }
);
Ok(true)
}
@@ -521,16 +497,17 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
let value = self.read_scalar(
value,
if matches!(ty.kind(), ty::Float(..)) {
- "a floating point number"
+ ExpectedKind::Float
} else {
- "an integer"
+ ExpectedKind::Int
},
)?;
// 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).
if matches!(value, Scalar::Ptr(..)) {
- throw_validation_failure!(self.path,
- { "{:x}", value } expected { "plain (non-pointer) bytes" }
+ throw_validation_failure!(
+ self.path,
+ ExpectedNonPtr { value: format!("{value:x}") }
)
}
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
// that lets us re-use `ref_to_mplace`).
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() {
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).
let layout = self.ecx.layout_of(*ty)?;
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)
}
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 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.path,
DanglingIntPointer(..) |
- InvalidFunctionPointer(..) =>
- { "{ptr}" } expected { "a function pointer" },
+ InvalidFunctionPointer(..) => InvalidFnPtr {
+ value: format!("{ptr}"),
+ },
);
// FIXME: Check if the signature matches
} else {
// Otherwise (for standalone Miri), we have to still check it to be non-null.
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)
}
- 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(..) => {
// Nothing to check.
Ok(true)
@@ -629,12 +607,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
if start == 1 && end == max_value {
// Only null is the niche. So make sure the ptr is NOT null.
if self.ecx.scalar_may_be_null(scalar)? {
- throw_validation_failure!(self.path,
- { "a potentially null pointer" }
- expected {
- "something that cannot possibly fail to be {}",
- wrapping_range_format(valid_range, max_value)
- }
+ throw_validation_failure!(
+ self.path,
+ NullablePtrOutOfRange { range: valid_range, max_value }
)
} else {
return Ok(());
@@ -645,12 +620,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
} else {
// Conservatively, we reject, because the pointer *could* have a bad
// value.
- throw_validation_failure!(self.path,
- { "a pointer" }
- expected {
- "something that cannot possibly fail to be {}",
- wrapping_range_format(valid_range, max_value)
- }
+ throw_validation_failure!(
+ self.path,
+ PtrOutOfRange { range: 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) {
Ok(())
} else {
- throw_validation_failure!(self.path,
- { "{}", bits }
- expected { "something {}", wrapping_range_format(valid_range, max_value) }
+ throw_validation_failure!(
+ self.path,
+ 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!(
this.ecx.read_discriminant(op),
this.path,
- InvalidTag(val) =>
- { "{:x}", val } expected { "a valid enum tag" },
- InvalidUninitBytes(None) =>
- { "uninitialized bytes" } expected { "a valid enum tag" },
+ InvalidTag(val) => InvalidEnumTag {
+ value: format!("{val:x}"),
+ },
+
+ InvalidUninitBytes(None) => UninitEnumTag,
)
.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.
if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. })) {
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(())
@@ -738,7 +711,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
#[inline]
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(())
}
@@ -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, .. }))
&& 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).
match op.layout.abi {
Abi::Uninhabited => {
- throw_validation_failure!(self.path,
- { "a value of uninhabited type {:?}", op.layout.ty }
- );
+ let ty = op.layout.ty;
+ throw_validation_failure!(self.path, UninhabitedVal { ty });
}
Abi::Scalar(scalar_layout) => {
if !scalar_layout.is_uninit_valid() {
// 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)?;
}
}
@@ -792,7 +764,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
// the other must be init.
if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() {
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(b, b_layout)?;
}
@@ -822,7 +794,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
try_validation!(
self.ecx.read_bytes_ptr_strip_provenance(mplace.ptr, Size::from_bytes(len)),
self.path,
- InvalidUninitBytes(..) => { "uninitialized data in `str`" },
+ InvalidUninitBytes(..) => { UninitStr },
);
}
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,
Right(imm) => match *imm {
Immediate::Uninit =>
- throw_validation_failure!(self.path, { "uninitialized bytes" }),
+ throw_validation_failure!(self.path, UninitVal),
Immediate::Scalar(..) | Immediate::ScalarPair(..) =>
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();
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).
@@ -929,12 +901,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
match visitor.visit_value(&op) {
Ok(()) => Ok(()),
// 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
// report them in a way that shows *where* in the value the issue lies.
Err(err) if matches!(err.kind(), InterpError::UndefinedBehavior(_)) => {
- err.print_backtrace();
- bug!("Unexpected Undefined Behavior error during validation: {}", err);
+ let (err, backtrace) = err.into_parts();
+ backtrace.print_backtrace();
+ bug!("Unexpected Undefined Behavior error during validation: {err:?}");
}
// Pass through everything else.
Err(err) => Err(err),
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index c36282d5e..c126f749b 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -4,6 +4,7 @@ Rust MIR: a lowered representation of Rust.
*/
+#![deny(rustc::untranslatable_diagnostic)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(decl_macro)]
@@ -33,6 +34,8 @@ pub mod interpret;
pub mod transform;
pub mod util;
+pub use errors::ReportErrorExt;
+
use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
use rustc_fluent_macro::fluent_messages;
use rustc_middle::query::Providers;
@@ -49,17 +52,11 @@ pub fn provide(providers: &mut Providers) {
let (param_env, raw) = param_env_and_value.into_parts();
const_eval::eval_to_valtree(tcx, param_env, raw)
};
- providers.try_destructure_mir_constant = |tcx, param_env_and_value| {
- let (param_env, value) = param_env_and_value.into_parts();
- const_eval::try_destructure_mir_constant(tcx, param_env, value).ok()
- };
+ providers.try_destructure_mir_constant_for_diagnostics =
+ |tcx, (cv, ty)| const_eval::try_destructure_mir_constant_for_diagnostics(tcx, cv, ty);
providers.valtree_to_const_val = |tcx, (ty, valtree)| {
const_eval::valtree_to_const_value(tcx, ty::ParamEnv::empty().and(ty), valtree)
};
- providers.deref_mir_constant = |tcx, param_env_and_value| {
- let (param_env, value) = param_env_and_value.into_parts();
- const_eval::deref_mir_constant(tcx, param_env, value)
- };
providers.check_validity_requirement = |tcx, (init_kind, param_env_and_ty)| {
util::check_validity_requirement(tcx, init_kind, param_env_and_ty)
};
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index 57d939747..14540e8df 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -9,8 +9,8 @@ use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
-use rustc_middle::ty::{self, adjustment::PointerCast, Instance, InstanceDef, Ty, TyCtxt};
-use rustc_middle::ty::{Binder, TraitRef, TypeVisitableExt};
+use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, InstanceDef, Ty, TyCtxt};
+use rustc_middle::ty::{TraitRef, TypeVisitableExt};
use rustc_mir_dataflow::{self, Analysis};
use rustc_span::{sym, Span, Symbol};
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
@@ -412,7 +412,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
BorrowKind::Shallow => {
PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow)
}
- BorrowKind::Unique => PlaceContext::MutatingUse(MutatingUseContext::Borrow),
BorrowKind::Mut { .. } => {
PlaceContext::MutatingUse(MutatingUseContext::Borrow)
}
@@ -457,7 +456,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}
}
- Rvalue::Ref(_, kind @ (BorrowKind::Mut { .. } | BorrowKind::Unique), place) => {
+ Rvalue::Ref(_, BorrowKind::Mut { .. }, place) => {
let ty = place.ty(self.body, self.tcx).ty;
let is_allowed = match ty.kind() {
// Inside a `static mut`, `&mut [...]` is allowed.
@@ -478,11 +477,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
};
if !is_allowed {
- if let BorrowKind::Mut { .. } = kind {
- self.check_mut_borrow(place.local, hir::BorrowKind::Ref)
- } else {
- self.check_op(ops::CellBorrow);
- }
+ self.check_mut_borrow(place.local, hir::BorrowKind::Ref)
}
}
@@ -526,12 +521,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}
Rvalue::Cast(
- CastKind::Pointer(
- PointerCast::MutToConstPointer
- | PointerCast::ArrayToPointer
- | PointerCast::UnsafeFnPointer
- | PointerCast::ClosureFnPointer(_)
- | PointerCast::ReifyFnPointer,
+ CastKind::PointerCoercion(
+ PointerCoercion::MutToConstPointer
+ | PointerCoercion::ArrayToPointer
+ | PointerCoercion::UnsafeFnPointer
+ | PointerCoercion::ClosureFnPointer(_)
+ | PointerCoercion::ReifyFnPointer,
),
_,
_,
@@ -539,7 +534,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
// These are all okay; they only change the type, not the data.
}
- Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), _, _) => {
+ Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize), _, _) => {
// Unsizing is implemented for CTFE.
}
@@ -617,30 +612,28 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}
fn visit_projection_elem(
&mut self,
- place_local: Local,
- proj_base: &[PlaceElem<'tcx>],
+ place_ref: PlaceRef<'tcx>,
elem: PlaceElem<'tcx>,
context: PlaceContext,
location: Location,
) {
trace!(
- "visit_projection_elem: place_local={:?} proj_base={:?} elem={:?} \
+ "visit_projection_elem: place_ref={:?} elem={:?} \
context={:?} location={:?}",
- place_local,
- proj_base,
+ place_ref,
elem,
context,
location,
);
- self.super_projection_elem(place_local, proj_base, elem, context, location);
+ self.super_projection_elem(place_ref, elem, context, location);
match elem {
ProjectionElem::Deref => {
- let base_ty = Place::ty_from(place_local, proj_base, self.body, self.tcx).ty;
+ let base_ty = place_ref.ty(self.body, self.tcx).ty;
if base_ty.is_unsafe_ptr() {
- if proj_base.is_empty() {
- let decl = &self.body.local_decls[place_local];
+ if place_ref.projection.is_empty() {
+ let decl = &self.body.local_decls[place_ref.local];
if let LocalInfo::StaticRef { def_id, .. } = *decl.local_info() {
let span = decl.source_info.span;
self.check_static(def_id, span);
@@ -702,7 +695,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
self.super_terminator(terminator, location);
match &terminator.kind {
- TerminatorKind::Call { func, args, fn_span, from_hir_call, .. } => {
+ TerminatorKind::Call { func, args, fn_span, call_source, .. } => {
let ConstCx { tcx, body, param_env, .. } = *self.ccx;
let caller = self.def_id();
@@ -755,17 +748,16 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
callee,
substs,
span: *fn_span,
- from_hir_call: *from_hir_call,
+ call_source: *call_source,
feature: Some(sym::const_trait_impl),
});
return;
}
let trait_ref = TraitRef::from_method(tcx, trait_id, substs);
- let poly_trait_pred =
- Binder::dummy(trait_ref).with_constness(ty::BoundConstness::ConstIfConst);
+ let trait_ref = trait_ref.with_constness(ty::BoundConstness::ConstIfConst);
let obligation =
- Obligation::new(tcx, ObligationCause::dummy(), param_env, poly_trait_pred);
+ Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref);
let implsrc = {
let infcx = tcx.infer_ctxt().build();
@@ -781,14 +773,23 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
);
return;
}
- Ok(Some(ImplSource::Closure(data))) => {
- if !tcx.is_const_fn_raw(data.closure_def_id) {
+ // Closure: Fn{Once|Mut}
+ Ok(Some(ImplSource::Builtin(_)))
+ if trait_ref.self_ty().is_closure()
+ && tcx.fn_trait_kind_from_def_id(trait_id).is_some() =>
+ {
+ let ty::Closure(closure_def_id, substs) =
+ *trait_ref.self_ty().kind()
+ else {
+ unreachable!()
+ };
+ if !tcx.is_const_fn_raw(closure_def_id) {
self.check_op(ops::FnCallNonConst {
caller,
callee,
substs,
span: *fn_span,
- from_hir_call: *from_hir_call,
+ call_source: *call_source,
feature: None,
});
@@ -814,7 +815,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
callee,
substs,
span: *fn_span,
- from_hir_call: *from_hir_call,
+ call_source: *call_source,
feature: None,
});
return;
@@ -838,7 +839,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
tcx,
ObligationCause::dummy_with_span(*fn_span),
param_env,
- poly_trait_pred,
+ trait_ref,
);
// improve diagnostics by showing what failed. Our requirements are stricter this time
@@ -857,7 +858,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
callee,
substs,
span: *fn_span,
- from_hir_call: *from_hir_call,
+ call_source: *call_source,
feature: None,
});
return;
@@ -917,7 +918,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
callee,
substs,
span: *fn_span,
- from_hir_call: *from_hir_call,
+ call_source: *call_source,
feature: None,
});
return;
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
index 21f3c2c89..4eb278252 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -2,18 +2,16 @@
use hir::def_id::LocalDefId;
use hir::{ConstContext, LangItem};
-use rustc_errors::{
- error_code, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed,
-};
+use rustc_errors::{error_code, DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
-use rustc_middle::mir;
+use rustc_middle::mir::{self, CallSource};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
+use rustc_middle::ty::TraitRef;
use rustc_middle::ty::{suggest_constraining_type_param, Adt, Closure, FnDef, FnPtr, Param, Ty};
-use rustc_middle::ty::{Binder, TraitRef};
use rustc_middle::util::{call_kind, CallDesugaringKind, CallKind};
use rustc_session::parse::feature_err;
use rustc_span::symbol::sym;
@@ -102,7 +100,7 @@ pub struct FnCallNonConst<'tcx> {
pub callee: DefId,
pub substs: SubstsRef<'tcx>,
pub span: Span,
- pub from_hir_call: bool,
+ pub call_source: CallSource,
pub feature: Option<Symbol>,
}
@@ -112,7 +110,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
ccx: &ConstCx<'_, 'tcx>,
_: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- let FnCallNonConst { caller, callee, substs, span, from_hir_call, feature } = *self;
+ let FnCallNonConst { caller, callee, substs, span, call_source, feature } = *self;
let ConstCx { tcx, param_env, .. } = *ccx;
let diag_trait = |err, self_ty: Ty<'_>, trait_id| {
@@ -139,12 +137,8 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
}
}
Adt(..) => {
- let obligation = Obligation::new(
- tcx,
- ObligationCause::dummy(),
- param_env,
- Binder::dummy(trait_ref),
- );
+ let obligation =
+ Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref);
let infcx = tcx.infer_ctxt().build();
let mut selcx = SelectionContext::new(&infcx);
@@ -152,40 +146,45 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
if let Ok(Some(ImplSource::UserDefined(data))) = implsrc {
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 });
}
}
_ => {}
}
};
- let call_kind = call_kind(tcx, ccx.param_env, callee, substs, span, from_hir_call, None);
+ let call_kind =
+ call_kind(tcx, ccx.param_env, callee, substs, span, call_source.from_hir_call(), None);
debug!(?call_kind);
let mut err = match call_kind {
CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => {
macro_rules! error {
- ($fmt:literal) => {
- struct_span_err!(tcx.sess, span, E0015, $fmt, self_ty, ccx.const_kind())
+ ($err:ident) => {
+ tcx.sess.create_err(errors::$err {
+ span,
+ ty: self_ty,
+ kind: ccx.const_kind(),
+ })
};
}
let mut err = match kind {
CallDesugaringKind::ForLoopIntoIter => {
- error!("cannot convert `{}` into an iterator in {}s")
+ error!(NonConstForLoopIntoIter)
}
CallDesugaringKind::QuestionBranch => {
- error!("`?` cannot determine the branch of `{}` in {}s")
+ error!(NonConstQuestionBranch)
}
CallDesugaringKind::QuestionFromResidual => {
- error!("`?` cannot convert from residual of `{}` in {}s")
+ error!(NonConstQuestionFromResidual)
}
CallDesugaringKind::TryBlockFromOutput => {
- error!("`try` block cannot convert `{}` to the result in {}s")
+ error!(NonConstTryBlockFromOutput)
}
CallDesugaringKind::Await => {
- error!("cannot convert `{}` into a future in {}s")
+ error!(NonConstAwait)
}
};
@@ -193,108 +192,101 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
err
}
CallKind::FnCall { fn_trait_id, self_ty } => {
- let mut err = struct_span_err!(
- tcx.sess,
- span,
- E0015,
- "cannot call non-const closure in {}s",
- ccx.const_kind(),
- );
-
- match self_ty.kind() {
+ let note = match self_ty.kind() {
FnDef(def_id, ..) => {
let span = tcx.def_span(*def_id);
if ccx.tcx.is_const_fn_raw(*def_id) {
span_bug!(span, "calling const FnDef errored when it shouldn't");
}
- err.span_note(span, "function defined here, but it is not `const`");
- }
- FnPtr(..) => {
- err.note(format!(
- "function pointers need an RFC before allowed to be called in {}s",
- ccx.const_kind()
- ));
+ Some(errors::NonConstClosureNote::FnDef { span })
}
- Closure(..) => {
- err.note(format!(
- "closures need an RFC before allowed to be called in {}s",
- ccx.const_kind()
- ));
- }
- _ => {}
- }
+ FnPtr(..) => Some(errors::NonConstClosureNote::FnPtr),
+ Closure(..) => Some(errors::NonConstClosureNote::Closure),
+ _ => None,
+ };
+
+ let mut err = tcx.sess.create_err(errors::NonConstClosure {
+ span,
+ kind: ccx.const_kind(),
+ note,
+ });
diag_trait(&mut err, self_ty, fn_trait_id);
err
}
CallKind::Operator { trait_id, self_ty, .. } => {
- let mut err = struct_span_err!(
- tcx.sess,
- span,
- E0015,
- "cannot call non-const operator in {}s",
- ccx.const_kind()
- );
-
- if Some(trait_id) == ccx.tcx.lang_items().eq_trait() {
- match (substs[0].unpack(), substs[1].unpack()) {
- (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
- if self_ty == rhs_ty
- && self_ty.is_ref()
- && self_ty.peel_refs().is_primitive() =>
- {
- let mut num_refs = 0;
- let mut tmp_ty = self_ty;
- while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
- num_refs += 1;
- tmp_ty = *inner_ty;
- }
- let deref = "*".repeat(num_refs);
-
- if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) {
- if let Some(eq_idx) = call_str.find("==") {
- if let Some(rhs_idx) =
- call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace())
- {
- let rhs_pos =
- span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx);
- let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
- err.multipart_suggestion(
- "consider dereferencing here",
- vec![
- (span.shrink_to_lo(), deref.clone()),
- (rhs_span, deref),
- ],
- Applicability::MachineApplicable,
- );
+ let mut err = if let CallSource::MatchCmp = call_source {
+ tcx.sess.create_err(errors::NonConstMatchEq {
+ span,
+ kind: ccx.const_kind(),
+ ty: self_ty,
+ })
+ } else {
+ let mut sugg = None;
+
+ if Some(trait_id) == ccx.tcx.lang_items().eq_trait() {
+ match (substs[0].unpack(), substs[1].unpack()) {
+ (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
+ if self_ty == rhs_ty
+ && self_ty.is_ref()
+ && self_ty.peel_refs().is_primitive() =>
+ {
+ let mut num_refs = 0;
+ let mut tmp_ty = self_ty;
+ while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
+ num_refs += 1;
+ tmp_ty = *inner_ty;
+ }
+ let deref = "*".repeat(num_refs);
+
+ if let Ok(call_str) =
+ ccx.tcx.sess.source_map().span_to_snippet(span)
+ {
+ if let Some(eq_idx) = call_str.find("==") {
+ if let Some(rhs_idx) = call_str[(eq_idx + 2)..]
+ .find(|c: char| !c.is_whitespace())
+ {
+ let rhs_pos = span.lo()
+ + BytePos::from_usize(eq_idx + 2 + rhs_idx);
+ let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
+ sugg = Some(errors::ConsiderDereferencing {
+ deref,
+ span: span.shrink_to_lo(),
+ rhs_span,
+ });
+ }
}
}
}
+ _ => {}
}
- _ => {}
}
- }
+ tcx.sess.create_err(errors::NonConstOperator {
+ span,
+ kind: ccx.const_kind(),
+ sugg,
+ })
+ };
diag_trait(&mut err, self_ty, trait_id);
err
}
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)
- if tcx.sess.source_map().is_span_accessible(deref_target) {
- err.span_note(deref_target, "deref defined here");
- }
+ let target = if tcx.sess.source_map().is_span_accessible(deref_target) {
+ 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)));
err
@@ -432,21 +424,12 @@ impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- let mut err = struct_span_err!(
- ccx.tcx.sess,
- span,
- E0493,
- "destructor of `{}` cannot be evaluated at compile-time",
- self.dropped_ty,
- );
- err.span_label(
+ ccx.tcx.sess.create_err(errors::LiveDrop {
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
+ dropped_ty: self.dropped_ty,
+ kind: ccx.const_kind(),
+ dropped_at: self.dropped_at,
+ })
}
}
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
index 1da205790..015a4aa94 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
@@ -172,7 +172,7 @@ impl Qualif for NeedsNonConstDrop {
if !matches!(
impl_src,
- ImplSource::ConstDestruct(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst)
+ ImplSource::Builtin(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst)
) {
// If our const destruct candidate is not ConstDestruct or implied by the param env,
// then it's bad
@@ -223,7 +223,7 @@ impl Qualif for CustomEq {
def: AdtDef<'tcx>,
substs: SubstsRef<'tcx>,
) -> bool {
- let ty = cx.tcx.mk_adt(def, substs);
+ let ty = Ty::new_adt(cx.tcx, def, substs);
!ty.is_structural_eq_shallow(cx.tcx)
}
}
@@ -344,15 +344,18 @@ where
};
// Check the qualifs of the value of `const` items.
- // FIXME(valtrees): check whether const qualifs should behave the same
- // way for type and mir constants.
let uneval = match constant.literal {
ConstantKind::Ty(ct)
- if matches!(ct.kind(), ty::ConstKind::Param(_) | ty::ConstKind::Error(_)) =>
+ if matches!(
+ ct.kind(),
+ ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_)
+ ) =>
{
None
}
- ConstantKind::Ty(c) => bug!("expected ConstKind::Param here, found {:?}", c),
+ ConstantKind::Ty(c) => {
+ bug!("expected ConstKind::Param or ConstKind::Value here, found {:?}", c)
+ }
ConstantKind::Unevaluated(uv, _) => Some(uv),
ConstantKind::Val(..) => None,
};
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
index 78c74e189..3a869f7f5 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
@@ -103,7 +103,7 @@ where
fn ref_allows_mutation(&self, kind: mir::BorrowKind, place: mir::Place<'tcx>) -> bool {
match kind {
mir::BorrowKind::Mut { .. } => true,
- mir::BorrowKind::Shared | mir::BorrowKind::Shallow | mir::BorrowKind::Unique => {
+ mir::BorrowKind::Shared | mir::BorrowKind::Shallow => {
self.shared_borrow_allows_mutation(place)
}
}
@@ -337,7 +337,7 @@ where
Q: Qualif,
{
fn apply_statement_effect(
- &self,
+ &mut self,
state: &mut Self::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
@@ -346,7 +346,7 @@ where
}
fn apply_terminator_effect(
- &self,
+ &mut self,
state: &mut Self::Domain,
terminator: &mir::Terminator<'tcx>,
location: Location,
@@ -355,7 +355,7 @@ where
}
fn apply_call_return_effect(
- &self,
+ &mut self,
state: &mut Self::Domain,
block: BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index 0e2d9ee8f..1b39a76e4 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -14,11 +14,10 @@
use rustc_hir as hir;
use rustc_middle::mir;
-use rustc_middle::mir::traversal::ReversePostorderIter;
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::subst::InternalSubsts;
-use rustc_middle::ty::{self, List, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{self, List, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::Span;
use rustc_index::{Idx, IndexSlice, IndexVec};
@@ -53,9 +52,8 @@ impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
return;
}
- let mut rpo = traversal::reverse_postorder(body);
let ccx = ConstCx::new(tcx, body);
- let (mut temps, all_candidates) = collect_temps_and_candidates(&ccx, &mut rpo);
+ let (mut temps, all_candidates) = collect_temps_and_candidates(&ccx);
let promotable_candidates = validate_candidates(&ccx, &mut temps, &all_candidates);
@@ -166,14 +164,13 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> {
pub fn collect_temps_and_candidates<'tcx>(
ccx: &ConstCx<'_, 'tcx>,
- rpo: &mut ReversePostorderIter<'_, 'tcx>,
) -> (IndexVec<Local, TempState>, Vec<Candidate>) {
let mut collector = Collector {
temps: IndexVec::from_elem(TempState::Undefined, &ccx.body.local_decls),
candidates: vec![],
ccx,
};
- for (bb, data) in rpo {
+ for (bb, data) in traversal::reverse_postorder(ccx.body) {
collector.visit_basic_block_data(bb, data);
}
(collector.temps, collector.candidates)
@@ -457,7 +454,9 @@ impl<'tcx> Validator<'_, 'tcx> {
match kind {
// Reject these borrow types just to be safe.
// FIXME(RalfJung): could we allow them? Should we? No point in it until we have a usecase.
- BorrowKind::Shallow | BorrowKind::Unique => return Err(Unpromotable),
+ BorrowKind::Shallow | BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture } => {
+ return Err(Unpromotable);
+ }
BorrowKind::Shared => {
let has_mut_interior = self.qualif_local::<qualifs::HasMutInterior>(place.local);
@@ -466,7 +465,9 @@ impl<'tcx> Validator<'_, 'tcx> {
}
}
- BorrowKind::Mut { .. } => {
+ // FIXME: consider changing this to only promote &mut [] for default borrows,
+ // also forbidding two phase borrows
+ BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow } => {
let ty = place.ty(self.body, self.tcx).ty;
// In theory, any zero-sized value could be borrowed
@@ -572,13 +573,18 @@ impl<'tcx> Validator<'_, 'tcx> {
| BinOp::Gt
| BinOp::Offset
| BinOp::Add
+ | BinOp::AddUnchecked
| BinOp::Sub
+ | BinOp::SubUnchecked
| BinOp::Mul
+ | BinOp::MulUnchecked
| BinOp::BitXor
| BinOp::BitAnd
| BinOp::BitOr
| BinOp::Shl
- | BinOp::Shr => {}
+ | BinOp::ShlUnchecked
+ | BinOp::Shr
+ | BinOp::ShrUnchecked => {}
}
self.validate_operand(lhs)?;
@@ -795,7 +801,9 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
};
match terminator.kind {
- TerminatorKind::Call { mut func, mut args, from_hir_call, fn_span, .. } => {
+ TerminatorKind::Call {
+ mut func, mut args, call_source: desugar, fn_span, ..
+ } => {
self.visit_operand(&mut func, loc);
for arg in &mut args {
self.visit_operand(arg, loc);
@@ -811,7 +819,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
unwind: UnwindAction::Continue,
destination: Place::from(new_temp),
target: Some(new_target),
- from_hir_call,
+ call_source: desugar,
fn_span,
},
source_info: SourceInfo::outermost(terminator.source_info.span),
@@ -859,7 +867,8 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
let ty = local_decls[place.local].ty;
let span = statement.source_info.span;
- let ref_ty = tcx.mk_ref(
+ let ref_ty = Ty::new_ref(
+ tcx,
tcx.lifetimes.re_erased,
ty::TypeAndMut { ty, mutbl: borrow_kind.to_mutbl_lossy() },
);
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 3c350e25b..4cc923cd9 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -67,8 +67,8 @@ impl<'tcx> MirPass<'tcx> for Validator {
unwind_edge_count: 0,
reachable_blocks: traversal::reachable_as_bitset(body),
storage_liveness,
- place_cache: Vec::new(),
- value_cache: Vec::new(),
+ place_cache: FxHashSet::default(),
+ value_cache: FxHashSet::default(),
};
checker.visit_body(body);
checker.check_cleanup_control_flow();
@@ -95,8 +95,8 @@ struct TypeChecker<'a, 'tcx> {
unwind_edge_count: usize,
reachable_blocks: BitSet<BasicBlock>,
storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive<'static>>,
- place_cache: Vec<PlaceRef<'tcx>>,
- value_cache: Vec<u128>,
+ place_cache: FxHashSet<PlaceRef<'tcx>>,
+ value_cache: FxHashSet<u128>,
}
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
@@ -318,8 +318,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
fn visit_projection_elem(
&mut self,
- local: Local,
- proj_base: &[PlaceElem<'tcx>],
+ place_ref: PlaceRef<'tcx>,
elem: PlaceElem<'tcx>,
context: PlaceContext,
location: Location,
@@ -334,7 +333,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
ProjectionElem::Deref
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::PostCleanup) =>
{
- let base_ty = Place::ty_from(local, proj_base, &self.body.local_decls, self.tcx).ty;
+ let base_ty = place_ref.ty(&self.body.local_decls, self.tcx).ty;
if base_ty.is_box() {
self.fail(
@@ -344,8 +343,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
ProjectionElem::Field(f, ty) => {
- let parent = Place { local, projection: self.tcx.mk_place_elems(proj_base) };
- let parent_ty = parent.ty(&self.body.local_decls, self.tcx);
+ let parent_ty = place_ref.ty(&self.body.local_decls, self.tcx);
let fail_out_of_bounds = |this: &Self, location| {
this.fail(location, format!("Out of bounds field {:?} for {:?}", f, parent_ty));
};
@@ -355,7 +353,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
location,
format!(
"Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is `{:?}`",
- parent, f, ty, f_ty
+ place_ref, f, ty, f_ty
)
)
}
@@ -434,7 +432,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
_ => {}
}
- self.super_projection_elem(local, proj_base, elem, context, location);
+ self.super_projection_elem(place_ref, elem, context, location);
}
fn visit_var_debug_info(&mut self, debuginfo: &VarDebugInfo<'tcx>) {
@@ -498,8 +496,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
macro_rules! check_kinds {
- ($t:expr, $text:literal, $($patterns:tt)*) => {
- if !matches!(($t).kind(), $($patterns)*) {
+ ($t:expr, $text:literal, $typat:pat) => {
+ if !matches!(($t).kind(), $typat) {
self.fail(location, format!($text, $t));
}
};
@@ -527,6 +525,25 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
use BinOp::*;
let a = vals.0.ty(&self.body.local_decls, self.tcx);
let b = vals.1.ty(&self.body.local_decls, self.tcx);
+ if crate::util::binop_right_homogeneous(*op) {
+ if let Eq | Lt | Le | Ne | Ge | Gt = op {
+ // The function pointer types can have lifetimes
+ if !self.mir_assign_valid_types(a, b) {
+ self.fail(
+ location,
+ format!("Cannot {op:?} compare incompatible types {a:?} and {b:?}"),
+ );
+ }
+ } else if a != b {
+ self.fail(
+ location,
+ format!(
+ "Cannot perform binary op {op:?} on unequal types {a:?} and {b:?}"
+ ),
+ );
+ }
+ }
+
match op {
Offset => {
check_kinds!(a, "Cannot offset non-pointer type {:?}", ty::RawPtr(..));
@@ -538,7 +555,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
for x in [a, b] {
check_kinds!(
x,
- "Cannot compare type {:?}",
+ "Cannot {op:?} compare type {:?}",
ty::Bool
| ty::Char
| ty::Int(..)
@@ -548,19 +565,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
| ty::FnPtr(..)
)
}
- // The function pointer types can have lifetimes
- if !self.mir_assign_valid_types(a, b) {
- self.fail(
- location,
- format!("Cannot compare unequal types {:?} and {:?}", a, b),
- );
- }
}
- Shl | Shr => {
+ AddUnchecked | SubUnchecked | MulUnchecked | Shl | ShlUnchecked | Shr
+ | ShrUnchecked => {
for x in [a, b] {
check_kinds!(
x,
- "Cannot shift non-integer type {:?}",
+ "Cannot {op:?} non-integer type {:?}",
ty::Uint(..) | ty::Int(..)
)
}
@@ -569,37 +580,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
for x in [a, b] {
check_kinds!(
x,
- "Cannot perform bitwise op on type {:?}",
+ "Cannot perform bitwise op {op:?} on type {:?}",
ty::Uint(..) | ty::Int(..) | ty::Bool
)
}
- if a != b {
- self.fail(
- location,
- format!(
- "Cannot perform bitwise op on unequal types {:?} and {:?}",
- a, b
- ),
- );
- }
}
Add | Sub | Mul | Div | Rem => {
for x in [a, b] {
check_kinds!(
x,
- "Cannot perform arithmetic on type {:?}",
+ "Cannot perform arithmetic {op:?} on type {:?}",
ty::Uint(..) | ty::Int(..) | ty::Float(..)
)
}
- if a != b {
- self.fail(
- location,
- format!(
- "Cannot perform arithmetic on unequal types {:?} and {:?}",
- a, b
- ),
- );
- }
}
}
}
@@ -657,7 +650,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
// FIXME: Add Checks for these
CastKind::PointerFromExposedAddress
| CastKind::PointerExposeAddress
- | CastKind::Pointer(_) => {}
+ | CastKind::PointerCoercion(_) => {}
CastKind::IntToInt | CastKind::IntToFloat => {
let input_valid = op_ty.is_integral() || op_ty.is_char() || op_ty.is_bool();
let target_valid = target_type.is_numeric() || target_type.is_char();
@@ -958,10 +951,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.value_cache.clear();
self.value_cache.extend(targets.iter().map(|(value, _)| value));
- let all_len = self.value_cache.len();
- self.value_cache.sort_unstable();
- self.value_cache.dedup();
- let has_duplicates = all_len != self.value_cache.len();
+ let has_duplicates = targets.iter().len() != self.value_cache.len();
if has_duplicates {
self.fail(
location,
@@ -994,16 +984,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
// passed by a reference to the callee. Consequently they must be non-overlapping.
// Currently this simply checks for duplicate places.
self.place_cache.clear();
- self.place_cache.push(destination.as_ref());
+ self.place_cache.insert(destination.as_ref());
+ let mut has_duplicates = false;
for arg in args {
if let Operand::Move(place) = arg {
- self.place_cache.push(place.as_ref());
+ has_duplicates |= !self.place_cache.insert(place.as_ref());
}
}
- let all_len = self.place_cache.len();
- let mut dedup = FxHashSet::default();
- self.place_cache.retain(|p| dedup.insert(*p));
- let has_duplicates = all_len != self.place_cache.len();
+
if has_duplicates {
self.fail(
location,
diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
index 23fcd22c5..2d1970791 100644
--- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
+++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
@@ -1,9 +1,8 @@
use rustc_middle::ty::layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement};
use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Ty, TyCtxt};
-use rustc_session::Limit;
use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants};
-use crate::const_eval::{CheckAlignment, CompileTimeInterpreter};
+use crate::const_eval::{CanAccessStatics, CheckAlignment, CompileTimeInterpreter};
use crate::interpret::{InterpCx, MemoryKind, OpTy};
/// Determines if this type permits "raw" initialization by just transmuting some memory into an
@@ -22,7 +21,7 @@ pub fn check_validity_requirement<'tcx>(
tcx: TyCtxt<'tcx>,
kind: ValidityRequirement,
param_env_and_ty: ParamEnvAnd<'tcx, Ty<'tcx>>,
-) -> Result<bool, LayoutError<'tcx>> {
+) -> Result<bool, &'tcx LayoutError<'tcx>> {
let layout = tcx.layout_of(param_env_and_ty)?;
// There is nothing strict or lax about inhabitedness.
@@ -44,12 +43,8 @@ fn might_permit_raw_init_strict<'tcx>(
ty: TyAndLayout<'tcx>,
tcx: TyCtxt<'tcx>,
kind: ValidityRequirement,
-) -> Result<bool, LayoutError<'tcx>> {
- let machine = CompileTimeInterpreter::new(
- Limit::new(0),
- /*can_access_statics:*/ false,
- CheckAlignment::Error,
- );
+) -> Result<bool, &'tcx LayoutError<'tcx>> {
+ let machine = CompileTimeInterpreter::new(CanAccessStatics::No, CheckAlignment::Error);
let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine);
@@ -80,7 +75,7 @@ fn might_permit_raw_init_lax<'tcx>(
this: TyAndLayout<'tcx>,
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
init_kind: ValidityRequirement,
-) -> Result<bool, LayoutError<'tcx>> {
+) -> Result<bool, &'tcx LayoutError<'tcx>> {
let scalar_allows_raw_init = move |s: Scalar| -> bool {
match init_kind {
ValidityRequirement::Inhabited => {
diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs
index 7641f5607..289e34225 100644
--- a/compiler/rustc_const_eval/src/util/mod.rs
+++ b/compiler/rustc_const_eval/src/util/mod.rs
@@ -1,3 +1,5 @@
+use rustc_middle::mir;
+
mod alignment;
mod check_validity_requirement;
mod compare_types;
@@ -7,3 +9,27 @@ pub use self::alignment::is_disaligned;
pub use self::check_validity_requirement::check_validity_requirement;
pub use self::compare_types::{is_equal_up_to_subtyping, is_subtype};
pub use self::type_name::type_name;
+
+/// Classify whether an operator is "left-homogeneous", i.e., the LHS has the
+/// same type as the result.
+#[inline]
+pub(crate) fn binop_left_homogeneous(op: mir::BinOp) -> bool {
+ use rustc_middle::mir::BinOp::*;
+ match op {
+ Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
+ | BitAnd | BitOr | Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => true,
+ Eq | Ne | Lt | Le | Gt | Ge => false,
+ }
+}
+
+/// Classify whether an operator is "right-homogeneous", i.e., the RHS has the
+/// same type as the LHS.
+#[inline]
+pub(crate) fn binop_right_homogeneous(op: mir::BinOp) -> bool {
+ use rustc_middle::mir::BinOp::*;
+ match op {
+ Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
+ | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true,
+ Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => false,
+ }
+}
diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs
index 11ad5b49d..4f01e0a24 100644
--- a/compiler/rustc_const_eval/src/util/type_name.rs
+++ b/compiler/rustc_const_eval/src/util/type_name.rs
@@ -63,6 +63,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
| ty::Generator(def_id, substs, _) => self.print_def_path(def_id, substs),
ty::Foreign(def_id) => self.print_def_path(def_id, &[]),
+ ty::Alias(ty::Weak, _) => bug!("type_name: unexpected weak projection"),
ty::Alias(ty::Inherent, _) => bug!("type_name: unexpected inherent projection"),
ty::GeneratorWitness(_) => bug!("type_name: unexpected `GeneratorWitness`"),
ty::GeneratorWitnessMIR(..) => bug!("type_name: unexpected `GeneratorWitnessMIR`"),