Skip to content

const interning: decide about mutability purely based on the kind of interning, not the types we see #116745

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 22 additions & 16 deletions compiler/rustc_const_eval/messages.ftl
Original file line number Diff line number Diff line change
@@ -46,8 +46,8 @@ 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_ptr_in_final = encountered dangling pointer in final value of {const_eval_intern_kind}
const_eval_dead_local =
accessing a dead local variable
const_eval_dealloc_immutable =
@@ -134,6 +134,14 @@ 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_intern_kind = {$kind ->
[static] static
[static_mut] mutable static
[const] constant
[promoted] promoted
*[other] {""}
}
const_eval_invalid_align =
align has to be a power of 2
@@ -205,6 +213,8 @@ const_eval_modified_global =
const_eval_mut_deref =
mutation through a reference is not allowed in {const_eval_const_context}s
const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
const_eval_non_const_fmt_macro_call =
cannot call non-const formatting macro in {const_eval_const_context}s
@@ -327,7 +337,7 @@ const_eval_too_many_caller_args =
const_eval_transient_mut_borrow = mutable references are not allowed in {const_eval_const_context}s
const_eval_transient_mut_borrow_raw = raw mutable references are not allowed in {const_eval_const_context}s
const_eval_transient_mut_raw = raw mutable pointers 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
@@ -341,21 +351,21 @@ const_eval_unallowed_heap_allocations =
const_eval_unallowed_inline_asm =
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 {const_eval_const_context}s
const_eval_unallowed_mutable_raw =
raw mutable pointers 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.
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 {const_eval_const_context}s
const_eval_unallowed_mutable_refs =
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.
@@ -392,9 +402,6 @@ const_eval_unstable_in_stable =
.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
@@ -406,7 +413,6 @@ const_eval_upcast_mismatch =
## The `front_matter`s here refer to either `const_eval_front_matter_invalid_value` or `const_eval_front_matter_invalid_value_with_path`.
## (We'd love to sort this differently to make that more clear but tidy won't let us...)
const_eval_validation_box_to_mut = {$front_matter}: encountered a box pointing to mutable memory in a constant
const_eval_validation_box_to_static = {$front_matter}: encountered a box pointing to a static variable in a constant
const_eval_validation_box_to_uninhabited = {$front_matter}: encountered a box pointing to uninhabited type {$ty}
const_eval_validation_dangling_box_no_provenance = {$front_matter}: encountered a dangling box ({$pointer} has no provenance)
@@ -441,7 +447,8 @@ const_eval_validation_invalid_fn_ptr = {$front_matter}: encountered {$value}, bu
const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object
const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object
const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer
const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in a `const`
const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in a `const` or `static`
const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory
const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!`
const_eval_validation_null_box = {$front_matter}: encountered a null box
const_eval_validation_null_fn_ptr = {$front_matter}: encountered a null function pointer
@@ -451,15 +458,14 @@ const_eval_validation_out_of_range = {$front_matter}: encountered {$value}, but
const_eval_validation_partial_pointer = {$front_matter}: encountered a partial pointer or a mix of pointers
const_eval_validation_pointer_as_int = {$front_matter}: encountered a pointer, but {$expected}
const_eval_validation_ptr_out_of_range = {$front_matter}: encountered a pointer, but expected something that cannot possibly fail to be {$in_range}
const_eval_validation_ref_to_mut = {$front_matter}: encountered a reference pointing to mutable memory in a constant
const_eval_validation_ref_to_static = {$front_matter}: encountered a reference pointing to a static variable in a constant
const_eval_validation_ref_to_uninhabited = {$front_matter}: encountered a reference pointing to uninhabited type {$ty}
const_eval_validation_unaligned_box = {$front_matter}: encountered an unaligned box (required {$required_bytes} byte alignment but found {$found_bytes})
const_eval_validation_unaligned_ref = {$front_matter}: encountered an unaligned reference (required {$required_bytes} byte alignment but found {$found_bytes})
const_eval_validation_uninhabited_enum_variant = {$front_matter}: encountered an uninhabited enum variant
const_eval_validation_uninhabited_val = {$front_matter}: encountered a value of uninhabited type `{$ty}`
const_eval_validation_uninit = {$front_matter}: encountered uninitialized memory, but {$expected}
const_eval_validation_unsafe_cell = {$front_matter}: encountered `UnsafeCell` in a `const`
const_eval_validation_unsafe_cell = {$front_matter}: encountered `UnsafeCell` in read-only memory
const_eval_write_through_immutable_pointer =
writing through a pointer that was derived from a shared (immutable) reference
32 changes: 19 additions & 13 deletions compiler/rustc_const_eval/src/const_eval/eval_queries.rs
Original file line number Diff line number Diff line change
@@ -313,6 +313,9 @@ pub fn eval_in_interpreter<'mir, 'tcx>(
cid: GlobalId<'tcx>,
is_static: bool,
) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> {
// `is_static` just means "in static", it could still be a promoted!
debug_assert_eq!(is_static, ecx.tcx.static_mutability(cid.instance.def_id()).is_some());
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oli-obk the is_static parameter here seems redundant, I couldn't quite figure out why we have it?

Copy link
Contributor

@oli-obk oli-obk Oct 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😆 just to avoid a second is_static invocation, which is pretty useless as it's only required in the diagnostics path


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) => {
@@ -350,8 +353,7 @@ pub fn eval_in_interpreter<'mir, 'tcx>(
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 =
const_validate_mplace(&ecx, &mplace, is_static, cid.promoted.is_some());
let validation = const_validate_mplace(&ecx, &mplace, cid);

let alloc_id = mplace.ptr().provenance.unwrap().alloc_id();

@@ -370,22 +372,26 @@ pub fn eval_in_interpreter<'mir, 'tcx>(
pub fn const_validate_mplace<'mir, 'tcx>(
ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
mplace: &MPlaceTy<'tcx>,
is_static: bool,
is_promoted: bool,
cid: GlobalId<'tcx>,
) -> InterpResult<'tcx> {
let mut ref_tracking = RefTracking::new(mplace.clone());
let mut inner = false;
while let Some((mplace, path)) = ref_tracking.todo.pop() {
let mode = if is_static {
if is_promoted {
// Promoteds in statics are allowed to point to statics.
CtfeValidationMode::Const { inner, allow_static_ptrs: true }
} else {
// a `static`
CtfeValidationMode::Regular
let mode = match ecx.tcx.static_mutability(cid.instance.def_id()) {
Some(_) if cid.promoted.is_some() => {
// Promoteds in statics are consts that re allowed to point to statics.
CtfeValidationMode::Const {
allow_immutable_unsafe_cell: false,
allow_static_ptrs: true,
}
}
Some(mutbl) => CtfeValidationMode::Static { mutbl }, // a `static`
None => {
// In normal `const` (not promoted), the outermost allocation is always only copied,
// so having `UnsafeCell` in there is okay despite them being in immutable memory.
let allow_immutable_unsafe_cell = cid.promoted.is_none() && !inner;
CtfeValidationMode::Const { allow_immutable_unsafe_cell, allow_static_ptrs: false }
}
} else {
CtfeValidationMode::Const { inner, allow_static_ptrs: false }
};
ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?;
inner = true;
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/const_eval/machine.rs
Original file line number Diff line number Diff line change
@@ -723,7 +723,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
&& ty.is_freeze(*ecx.tcx, ecx.param_env)
{
let place = ecx.ref_to_mplace(val)?;
let new_place = place.map_provenance(|p| p.map(CtfeProvenance::as_immutable));
let new_place = place.map_provenance(CtfeProvenance::as_immutable);
Ok(ImmTy::from_immediate(new_place.to_ref(ecx), val.layout))
} else {
Ok(val.clone())
51 changes: 33 additions & 18 deletions compiler/rustc_const_eval/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::borrow::Cow;

use rustc_errors::{
DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Handler,
IntoDiagnostic,
@@ -13,12 +15,24 @@ use rustc_middle::ty::{self, Ty};
use rustc_span::Span;
use rustc_target::abi::call::AdjustForForeignAbiError;
use rustc_target::abi::{Size, WrappingRange};
use rustc_type_ir::Mutability;

use crate::interpret::InternKind;

#[derive(Diagnostic)]
#[diag(const_eval_dangling_ptr_in_final)]
pub(crate) struct DanglingPtrInFinal {
#[primary_span]
pub span: Span,
pub kind: InternKind,
}

#[derive(Diagnostic)]
#[diag(const_eval_mutable_ptr_in_final)]
pub(crate) struct MutablePtrInFinal {
#[primary_span]
pub span: Span,
pub kind: InternKind,
}

#[derive(Diagnostic)]
@@ -100,8 +114,8 @@ pub(crate) struct TransientMutBorrowErr {
}

#[derive(Diagnostic)]
#[diag(const_eval_transient_mut_borrow_raw, code = "E0658")]
pub(crate) struct TransientMutBorrowErrRaw {
#[diag(const_eval_transient_mut_raw, code = "E0658")]
pub(crate) struct TransientMutRawErr {
#[primary_span]
pub span: Span,
pub kind: ConstContext,
@@ -142,8 +156,8 @@ pub(crate) struct UnallowedMutableRefs {
}

#[derive(Diagnostic)]
#[diag(const_eval_unallowed_mutable_refs_raw, code = "E0764")]
pub(crate) struct UnallowedMutableRefsRaw {
#[diag(const_eval_unallowed_mutable_raw, code = "E0764")]
pub(crate) struct UnallowedMutableRaw {
#[primary_span]
pub span: Span,
pub kind: ConstContext,
@@ -194,14 +208,6 @@ pub(crate) struct UnallowedInlineAsm {
pub kind: ConstContext,
}

#[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 {
@@ -619,18 +625,16 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
PtrToStatic { ptr_kind: PointerKind::Box } => const_eval_validation_box_to_static,
PtrToStatic { ptr_kind: PointerKind::Ref } => const_eval_validation_ref_to_static,

PtrToMut { ptr_kind: PointerKind::Box } => const_eval_validation_box_to_mut,
PtrToMut { ptr_kind: PointerKind::Ref } => const_eval_validation_ref_to_mut,

PointerAsInt { .. } => const_eval_validation_pointer_as_int,
PartialPointer => const_eval_validation_partial_pointer,
MutableRefInConst => const_eval_validation_mutable_ref_in_const,
MutableRefToImmutable => const_eval_validation_mutable_ref_to_immutable,
NullFnPtr => const_eval_validation_null_fn_ptr,
NeverVal => const_eval_validation_never_val,
NullablePtrOutOfRange { .. } => const_eval_validation_nullable_ptr_out_of_range,
PtrOutOfRange { .. } => const_eval_validation_ptr_out_of_range,
OutOfRange { .. } => const_eval_validation_out_of_range,
UnsafeCell => const_eval_validation_unsafe_cell,
UnsafeCellInImmutable => const_eval_validation_unsafe_cell,
UninhabitedVal { .. } => const_eval_validation_uninhabited_val,
InvalidEnumTag { .. } => const_eval_validation_invalid_enum_tag,
UninhabitedEnumVariant => const_eval_validation_uninhabited_enum_variant,
@@ -776,11 +780,11 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
}
NullPtr { .. }
| PtrToStatic { .. }
| PtrToMut { .. }
| MutableRefInConst
| MutableRefToImmutable
| NullFnPtr
| NeverVal
| UnsafeCell
| UnsafeCellInImmutable
| InvalidMetaSliceTooLarge { .. }
| InvalidMetaTooLarge { .. }
| DanglingPtrUseAfterFree { .. }
@@ -906,3 +910,14 @@ impl ReportErrorExt for ResourceExhaustionInfo {
}
fn add_args<G: EmissionGuarantee>(self, _: &Handler, _: &mut DiagnosticBuilder<'_, G>) {}
}

impl rustc_errors::IntoDiagnosticArg for InternKind {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Borrowed(match self {
InternKind::Static(Mutability::Not) => "static",
InternKind::Static(Mutability::Mut) => "static_mut",
InternKind::Constant => "const",
InternKind::Promoted => "promoted",
}))
}
}
557 changes: 150 additions & 407 deletions compiler/rustc_const_eval/src/interpret/intern.rs

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions compiler/rustc_const_eval/src/interpret/place.rs
Original file line number Diff line number Diff line change
@@ -62,8 +62,8 @@ pub(super) struct MemPlace<Prov: Provenance = CtfeProvenance> {

impl<Prov: Provenance> MemPlace<Prov> {
/// Adjust the provenance of the main pointer (metadata is unaffected).
pub fn map_provenance(self, f: impl FnOnce(Option<Prov>) -> Option<Prov>) -> Self {
MemPlace { ptr: self.ptr.map_provenance(f), ..self }
pub fn map_provenance(self, f: impl FnOnce(Prov) -> Prov) -> Self {
MemPlace { ptr: self.ptr.map_provenance(|p| p.map(f)), ..self }
}

/// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space.
@@ -128,7 +128,7 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
}

/// Adjust the provenance of the main pointer (metadata is unaffected).
pub fn map_provenance(self, f: impl FnOnce(Option<Prov>) -> Option<Prov>) -> Self {
pub fn map_provenance(self, f: impl FnOnce(Prov) -> Prov) -> Self {
MPlaceTy { mplace: self.mplace.map_provenance(f), ..self }
}

145 changes: 116 additions & 29 deletions compiler/rustc_const_eval/src/interpret/validity.rs
Original file line number Diff line number Diff line change
@@ -9,12 +9,13 @@ use std::num::NonZeroUsize;

use either::{Left, Right};

use hir::def::DefKind;
use rustc_ast::Mutability;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_middle::mir::interpret::{
ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, ValidationErrorInfo,
ValidationErrorKind, ValidationErrorKind::*,
ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance,
ValidationErrorInfo, ValidationErrorKind, ValidationErrorKind::*,
};
use rustc_middle::ty;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
@@ -123,15 +124,41 @@ pub enum PathElem {
}

/// Extra things to check for during validation of CTFE results.
#[derive(Copy, Clone)]
pub enum CtfeValidationMode {
/// Regular validation, nothing special happening.
Regular,
/// Validation of a `const`.
/// `inner` says if this is an inner, indirect allocation (as opposed to the top-level const
/// allocation). Being an inner allocation makes a difference because the top-level allocation
/// of a `const` is copied for each use, but the inner allocations are implicitly shared.
/// Validation of a `static`
Static { mutbl: Mutability },
/// Validation of a `const` (including promoteds).
/// `allow_immutable_unsafe_cell` says whether we allow `UnsafeCell` in immutable memory (which is the
/// case for the top-level allocation of a `const`, where this is fine because the allocation will be
/// copied at each use site).
/// `allow_static_ptrs` says if pointers to statics are permitted (which is the case for promoteds in statics).
Const { inner: bool, allow_static_ptrs: bool },
Const { allow_immutable_unsafe_cell: bool, allow_static_ptrs: bool },
}

impl CtfeValidationMode {
fn allow_immutable_unsafe_cell(self) -> bool {
match self {
CtfeValidationMode::Static { .. } => false,
CtfeValidationMode::Const { allow_immutable_unsafe_cell, .. } => {
allow_immutable_unsafe_cell
}
}
}

fn allow_static_ptrs(self) -> bool {
match self {
CtfeValidationMode::Static { .. } => true, // statics can point to statics
CtfeValidationMode::Const { allow_static_ptrs, .. } => allow_static_ptrs,
}
}

fn may_contain_mutable_ref(self) -> bool {
match self {
CtfeValidationMode::Static { mutbl } => mutbl == Mutability::Mut,
CtfeValidationMode::Const { .. } => false,
}
}
}

/// State for tracking recursive validation of references
@@ -418,6 +445,22 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
}
// Recursive checking
if let Some(ref_tracking) = self.ref_tracking.as_deref_mut() {
// Determine whether this pointer expects to be pointing to something mutable.
let ptr_expected_mutbl = match ptr_kind {
PointerKind::Box => Mutability::Mut,
PointerKind::Ref => {
let tam = value.layout.ty.builtin_deref(false).unwrap();
// ZST never require mutability. We do not take into account interior mutability
// here since we cannot know if there really is an `UnsafeCell` inside
// `Option<UnsafeCell>` -- so we check that in the recursive descent behind this
// reference.
if size == Size::ZERO || tam.mutbl == Mutability::Not {
Mutability::Not
} else {
Mutability::Mut
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could have an extra check for matches!(self.ctfe_mode, Some(Const(..))) which then rejects mutable references immediately... but we already intern all allocations as immutable and so such code will already error, just with a different error message ("mutable ref in const" vs "dangling ref"/"mutable ref to immutable memory").

}
}
};
// Proceed recursively even for ZST, no reason to skip them!
// `!` is a ZST and we want to validate it.
if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr()) {
@@ -428,16 +471,29 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
// Special handling for pointers to statics (irrespective of their type).
assert!(!self.ecx.tcx.is_thread_local_static(did));
assert!(self.ecx.tcx.is_static(did));
if matches!(
self.ctfe_mode,
Some(CtfeValidationMode::Const { allow_static_ptrs: false, .. })
) {
if self.ctfe_mode.is_some_and(|c| !c.allow_static_ptrs()) {
// See const_eval::machine::MemoryExtra::can_access_statics for why
// 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, PtrToStatic { ptr_kind });
}
// Mutability check.
if ptr_expected_mutbl == Mutability::Mut {
if matches!(
self.ecx.tcx.def_kind(did),
DefKind::Static(Mutability::Not)
) && self
.ecx
.tcx
.type_of(did)
.no_bound_vars()
.expect("statics should not have generic parameters")
.is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all())
{
throw_validation_failure!(self.path, MutableRefToImmutable);
}
}
// 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
// unsafe code.
@@ -454,14 +510,29 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
if alloc.inner().mutability == Mutability::Mut
&& matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. }))
{
// 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, PtrToMut { ptr_kind });
// This is impossible: this can only be some inner allocation of a
// `static mut` (everything else either hits the `GlobalAlloc::Static`
// case or is interned immutably). To get such a pointer we'd have to
// load it from a static, but such loads lead to a CTFE error.
span_bug!(
self.ecx.tcx.span,
"encountered reference to mutable memory inside a `const`"
);
}
if ptr_expected_mutbl == Mutability::Mut
&& alloc.inner().mutability == Mutability::Not
{
throw_validation_failure!(self.path, MutableRefToImmutable);
}
}
// Nothing to check for these.
None | Some(GlobalAlloc::Function(..) | GlobalAlloc::VTable(..)) => {}
Some(GlobalAlloc::Function(..) | GlobalAlloc::VTable(..)) => {
// These are immutable, we better don't allow mutable pointers here.
if ptr_expected_mutbl == Mutability::Mut {
throw_validation_failure!(self.path, MutableRefToImmutable);
}
}
// Dangling, already handled.
None => bug!(),
}
}
let path = &self.path;
@@ -532,11 +603,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
Ok(true)
}
ty::Ref(_, ty, mutbl) => {
if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. }))
if self.ctfe_mode.is_some_and(|c| !c.may_contain_mutable_ref())
&& *mutbl == Mutability::Mut
{
// A mutable reference inside a const? That does not seem right (except if it is
// a ZST).
let layout = self.ecx.layout_of(*ty)?;
if !layout.is_zst() {
throw_validation_failure!(self.path, MutableRefInConst);
@@ -642,6 +711,19 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
)
}
}

fn in_mutable_memory(&self, op: &OpTy<'tcx, M::Provenance>) -> bool {
if let Some(mplace) = op.as_mplace_or_imm().left() {
if let Some(alloc_id) = mplace.ptr().provenance.and_then(|p| p.get_alloc_id()) {
if self.ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner().mutability
== Mutability::Mut
{
return true;
}
}
}
false
}
}

impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
@@ -705,10 +787,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
op: &OpTy<'tcx, M::Provenance>,
_fields: NonZeroUsize,
) -> InterpResult<'tcx> {
// Special check preventing `UnsafeCell` inside unions in the inner part of constants.
if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. })) {
if !op.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.param_env) {
throw_validation_failure!(self.path, UnsafeCell);
// Special check for CTFE validation, preventing `UnsafeCell` inside unions in immutable memory.
if self.ctfe_mode.is_some_and(|c| !c.allow_immutable_unsafe_cell()) {
if !op.layout.is_zst() && !op.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.param_env) {
if !self.in_mutable_memory(op) {
throw_validation_failure!(self.path, UnsafeCellInImmutable);
}
}
}
Ok(())
@@ -730,11 +814,14 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
}

// Special check preventing `UnsafeCell` in the inner part of constants
if let Some(def) = op.layout.ty.ty_adt_def() {
if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. }))
if self.ctfe_mode.is_some_and(|c| !c.allow_immutable_unsafe_cell()) {
if !op.layout.is_zst()
&& let Some(def) = op.layout.ty.ty_adt_def()
&& def.is_unsafe_cell()
{
throw_validation_failure!(self.path, UnsafeCell);
if !self.in_mutable_memory(op) {
throw_validation_failure!(self.path, UnsafeCellInImmutable);
}
}
}

10 changes: 5 additions & 5 deletions compiler/rustc_const_eval/src/transform/check_consts/ops.rs
Original file line number Diff line number Diff line change
@@ -508,7 +508,7 @@ impl<'tcx> NonConstOp<'tcx> for MutBorrow {
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
match self.0 {
hir::BorrowKind::Raw => ccx.tcx.sess.create_err(errors::UnallowedMutableRefsRaw {
hir::BorrowKind::Raw => ccx.tcx.sess.create_err(errors::UnallowedMutableRaw {
span,
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(&error_code!(E0764)).then_some(()),
@@ -537,10 +537,10 @@ impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow {
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let kind = ccx.const_kind();
match self.0 {
hir::BorrowKind::Raw => ccx.tcx.sess.create_feature_err(
errors::TransientMutBorrowErrRaw { span, kind },
sym::const_mut_refs,
),
hir::BorrowKind::Raw => ccx
.tcx
.sess
.create_feature_err(errors::TransientMutRawErr { span, kind }, sym::const_mut_refs),
hir::BorrowKind::Ref => ccx.tcx.sess.create_feature_err(
errors::TransientMutBorrowErr { span, kind },
sym::const_mut_refs,
1 change: 1 addition & 0 deletions compiler/rustc_const_eval/src/util/caller_location.rs
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@ fn alloc_caller_location<'mir, 'tcx>(
// See https://github.com/rust-lang/rust/pull/89920#discussion_r730012398
ecx.allocate_str("<redacted>", MemoryKind::CallerLocation, Mutability::Not).unwrap()
};
let file = file.map_provenance(CtfeProvenance::as_immutable);
let line = if loc_details.line { Scalar::from_u32(line) } else { Scalar::from_u32(0) };
let col = if loc_details.column { Scalar::from_u32(col) } else { Scalar::from_u32(0) };

4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/mir/interpret/error.rs
Original file line number Diff line number Diff line change
@@ -427,14 +427,14 @@ pub enum ValidationErrorKind<'tcx> {
PartialPointer,
PtrToUninhabited { ptr_kind: PointerKind, ty: Ty<'tcx> },
PtrToStatic { ptr_kind: PointerKind },
PtrToMut { ptr_kind: PointerKind },
MutableRefInConst,
MutableRefToImmutable,
UnsafeCellInImmutable,
NullFnPtr,
NeverVal,
NullablePtrOutOfRange { range: WrappingRange, max_value: u128 },
PtrOutOfRange { range: WrappingRange, max_value: u128 },
OutOfRange { value: String, range: WrappingRange, max_value: u128 },
UnsafeCell,
UninhabitedVal { ty: Ty<'tcx> },
InvalidEnumTag { value: String },
UninhabitedEnumVariant,
6 changes: 6 additions & 0 deletions compiler/rustc_middle/src/mir/interpret/pointer.rs
Original file line number Diff line number Diff line change
@@ -277,6 +277,12 @@ impl From<AllocId> for Pointer {
Pointer::new(alloc_id.into(), Size::ZERO)
}
}
impl From<CtfeProvenance> for Pointer {
#[inline(always)]
fn from(prov: CtfeProvenance) -> Self {
Pointer::new(prov, Size::ZERO)
}
}

impl<Prov> From<Pointer<Prov>> for Pointer<Option<Prov>> {
#[inline(always)]
4 changes: 3 additions & 1 deletion src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
Original file line number Diff line number Diff line change
@@ -829,7 +829,9 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
let new_prov = this.sb_reborrow(place, size, new_perm, new_tag, info)?;

// Adjust place.
Ok(place.clone().map_provenance(|_| new_prov))
// (If the closure gets called, that means the old provenance was `Some`, and hence the new
// one must also be `Some`.)
Ok(place.clone().map_provenance(|_| new_prov.unwrap()))
}

/// Retags an individual pointer, returning the retagged version.
4 changes: 3 additions & 1 deletion src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
Original file line number Diff line number Diff line change
@@ -353,7 +353,9 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
let new_prov = this.tb_reborrow(place, reborrow_size, new_perm, new_tag)?;

// Adjust place.
Ok(place.clone().map_provenance(|_| new_prov))
// (If the closure gets called, that means the old provenance was `Some`, and hence the new
// one must also be `Some`.)
Ok(place.clone().map_provenance(|_| new_prov.unwrap()))
}

/// Retags an individual pointer, returning the retagged version.
8 changes: 4 additions & 4 deletions tests/ui/consts/const-address-of-mut.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#![feature(raw_ref_op)]

const A: () = { let mut x = 2; &raw mut x; }; //~ mutable reference
const A: () = { let mut x = 2; &raw mut x; }; //~ mutable pointer

static B: () = { let mut x = 2; &raw mut x; }; //~ mutable reference
static B: () = { let mut x = 2; &raw mut x; }; //~ mutable pointer

static mut C: () = { let mut x = 2; &raw mut x; }; //~ mutable reference
static mut C: () = { let mut x = 2; &raw mut x; }; //~ mutable pointer

const fn foo() {
let mut x = 0;
let y = &raw mut x; //~ mutable reference
let y = &raw mut x; //~ mutable pointer
}

fn main() {}
8 changes: 4 additions & 4 deletions tests/ui/consts/const-address-of-mut.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0658]: raw mutable references are not allowed in constants
error[E0658]: raw mutable pointers are not allowed in constants
--> $DIR/const-address-of-mut.rs:3:32
|
LL | const A: () = { let mut x = 2; &raw mut x; };
@@ -7,7 +7,7 @@ LL | const A: () = { let mut x = 2; &raw mut x; };
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable

error[E0658]: raw mutable references are not allowed in statics
error[E0658]: raw mutable pointers are not allowed in statics
--> $DIR/const-address-of-mut.rs:5:33
|
LL | static B: () = { let mut x = 2; &raw mut x; };
@@ -16,7 +16,7 @@ LL | static B: () = { let mut x = 2; &raw mut x; };
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable

error[E0658]: raw mutable references are not allowed in statics
error[E0658]: raw mutable pointers are not allowed in statics
--> $DIR/const-address-of-mut.rs:7:37
|
LL | static mut C: () = { let mut x = 2; &raw mut x; };
@@ -25,7 +25,7 @@ LL | static mut C: () = { let mut x = 2; &raw mut x; };
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable

error[E0658]: raw mutable references are not allowed in constant functions
error[E0658]: raw mutable pointers are not allowed in constant functions
--> $DIR/const-address-of-mut.rs:11:13
|
LL | let y = &raw mut x;
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
use std::intrinsics;

const FOO: &i32 = foo();
const FOO_RAW: *const i32 = foo();

const fn foo() -> &'static i32 {
let t = unsafe {
@@ -15,5 +16,6 @@ const fn foo() -> &'static i32 {
unsafe { &*t }
}
fn main() {
assert_eq!(*FOO, 20)
assert_eq!(*FOO, 20);
assert_eq!(unsafe { *FOO_RAW }, 20);
}

This file was deleted.

This file was deleted.

2 changes: 1 addition & 1 deletion tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.rs
Original file line number Diff line number Diff line change
@@ -4,6 +4,6 @@
use std::intrinsics;

const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 };
//~^ error: unsupported untyped pointer in constant
//~^ error: mutable pointer in final value of constant

fn main() {}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
error: unsupported untyped pointer in constant
error: encountered mutable pointer in final value of constant
--> $DIR/alloc_intrinsic_untyped.rs:6:1
|
LL | const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 };
| ^^^^^^^^^^^^^^^^^^^
|
= note: memory only reachable via raw pointers is not supported

error: aborting due to 1 previous error

Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
use std::intrinsics;

const _X: &'static u8 = unsafe {
//~^ error: dangling pointer in final constant
//~^ error: dangling pointer in final value of constant
let ptr = intrinsics::const_allocate(4, 4);
intrinsics::const_deallocate(ptr, 4, 4);
&*ptr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: encountered dangling pointer in final constant
error: encountered dangling pointer in final value of constant
--> $DIR/dealloc_intrinsic_dangling.rs:7:1
|
LL | const _X: &'static u8 = unsafe {
15 changes: 15 additions & 0 deletions tests/ui/consts/const-mut-refs/mut_ref_in_final.rs
Original file line number Diff line number Diff line change
@@ -44,6 +44,21 @@ static mut FOO3: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42));
// the enclosing scope rule.
const BAR: NotAMutex<&i32> = NotAMutex(UnsafeCell::new(&42));

struct SyncPtr<T> { x : *const T }
unsafe impl<T> Sync for SyncPtr<T> {}

// These pass the lifetime checks because of the "tail expression" / "outer scope" rule.
// (This relies on `SyncPtr` being a curly brace struct.)
// However, we intern the inner memory as read-only, so this must be rejected.
static RAW_MUT_CAST_S: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
//~^ ERROR mutable references are not allowed
static RAW_MUT_COERCE_S: SyncPtr<i32> = SyncPtr { x: &mut 0 };
//~^ ERROR mutable references are not allowed
const RAW_MUT_CAST_C: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
//~^ ERROR mutable references are not allowed
const RAW_MUT_COERCE_C: SyncPtr<i32> = SyncPtr { x: &mut 0 };
//~^ ERROR mutable references are not allowed

fn main() {
println!("{}", unsafe { *A });
unsafe { *B = 4 } // Bad news
26 changes: 25 additions & 1 deletion tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr
Original file line number Diff line number Diff line change
@@ -54,7 +54,31 @@ LL | static mut FOO3: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42));
| | creates a temporary value which is freed while still in use
| using this value as a static requires that borrow lasts for `'static`

error: aborting due to 6 previous errors
error[E0764]: mutable references are not allowed in the final value of statics
--> $DIR/mut_ref_in_final.rs:53:53
|
LL | static RAW_MUT_CAST_S: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
| ^^^^^^^

error[E0764]: mutable references are not allowed in the final value of statics
--> $DIR/mut_ref_in_final.rs:55:54
|
LL | static RAW_MUT_COERCE_S: SyncPtr<i32> = SyncPtr { x: &mut 0 };
| ^^^^^^

error[E0764]: mutable references are not allowed in the final value of constants
--> $DIR/mut_ref_in_final.rs:57:52
|
LL | const RAW_MUT_CAST_C: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
| ^^^^^^^

error[E0764]: mutable references are not allowed in the final value of constants
--> $DIR/mut_ref_in_final.rs:59:53
|
LL | const RAW_MUT_COERCE_C: SyncPtr<i32> = SyncPtr { x: &mut 0 };
| ^^^^^^

error: aborting due to 10 previous errors

Some errors have detailed explanations: E0716, E0764.
For more information about an error, try `rustc --explain E0716`.
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
error[E0080]: it is undefined behavior to use this value
--> $DIR/mut_ref_in_final_dynamic_check.rs:17:1
--> $DIR/mut_ref_in_final_dynamic_check.rs:15:1
|
LL | const A: Option<&mut i32> = helper();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(Some)>.0: encountered mutable reference in a `const`
| ^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(Some)>.0: encountered mutable reference in a `const` or `static`
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 4, align: 4) {
2a 00 00 00 │ *...
}

error: encountered dangling pointer in final constant
--> $DIR/mut_ref_in_final_dynamic_check.rs:24:1
error: encountered dangling pointer in final value of constant
--> $DIR/mut_ref_in_final_dynamic_check.rs:22:1
|
LL | const B: Option<&mut i32> = helper2();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
error[E0080]: it is undefined behavior to use this value
--> $DIR/mut_ref_in_final_dynamic_check.rs:17:1
--> $DIR/mut_ref_in_final_dynamic_check.rs:15:1
|
LL | const A: Option<&mut i32> = helper();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(Some)>.0: encountered mutable reference in a `const`
| ^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(Some)>.0: encountered mutable reference in a `const` or `static`
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 8) {
2a 00 00 00 00 00 00 00 │ *.......
}

error: encountered dangling pointer in final constant
--> $DIR/mut_ref_in_final_dynamic_check.rs:24:1
error: encountered dangling pointer in final value of constant
--> $DIR/mut_ref_in_final_dynamic_check.rs:22:1
|
LL | const B: Option<&mut i32> = helper2();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
Original file line number Diff line number Diff line change
@@ -12,15 +12,13 @@ const fn helper() -> Option<&'static mut i32> { unsafe {
// Undefined behaviour (integer as pointer), who doesn't love tests like this.
Some(&mut *(42 as *mut i32))
} }
// The error is an evaluation error and not a validation error, so the error is reported
// directly at the site where it occurs.
const A: Option<&mut i32> = helper(); //~ ERROR it is undefined behavior to use this value
//~^ encountered mutable reference in a `const`

const fn helper2() -> Option<&'static mut i32> { unsafe {
// Undefined behaviour (dangling pointer), who doesn't love tests like this.
Some(&mut *(&mut 42 as *mut i32))
} }
const B: Option<&mut i32> = helper2(); //~ ERROR encountered dangling pointer in final constant
const B: Option<&mut i32> = helper2(); //~ ERROR encountered dangling pointer in final value of constant

fn main() {}
22 changes: 0 additions & 22 deletions tests/ui/consts/const-points-to-static.32bit.stderr

This file was deleted.

22 changes: 0 additions & 22 deletions tests/ui/consts/const-points-to-static.64bit.stderr

This file was deleted.

13 changes: 0 additions & 13 deletions tests/ui/consts/const-points-to-static.rs

This file was deleted.

2 changes: 1 addition & 1 deletion tests/ui/consts/dangling-alloc-id-ice.rs
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ union Foo<'a> {
}

const FOO: &() = {
//~^ ERROR encountered dangling pointer in final constant
//~^ ERROR encountered dangling pointer in final value of constant
let y = ();
unsafe { Foo { y: &y }.long_live_the_unit }
};
2 changes: 1 addition & 1 deletion tests/ui/consts/dangling-alloc-id-ice.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: encountered dangling pointer in final constant
error: encountered dangling pointer in final value of constant
--> $DIR/dangling-alloc-id-ice.rs:8:1
|
LL | const FOO: &() = {
2 changes: 1 addition & 1 deletion tests/ui/consts/dangling_raw_ptr.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const FOO: *const u32 = { //~ ERROR encountered dangling pointer in final constant
const FOO: *const u32 = { //~ ERROR encountered dangling pointer in final value of constant
let x = 42;
&x
};
2 changes: 1 addition & 1 deletion tests/ui/consts/dangling_raw_ptr.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: encountered dangling pointer in final constant
error: encountered dangling pointer in final value of constant
--> $DIR/dangling_raw_ptr.rs:1:1
|
LL | const FOO: *const u32 = {
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
error[E0080]: it is undefined behavior to use this value
--> $DIR/invalid-union.rs:35:1
--> $DIR/interior-mut-const-via-union.rs:35:1
|
LL | fn main() {
| ^^^^^^^^^ constructing invalid value at .<deref>.y.<enum-variant(B)>.0: encountered `UnsafeCell` in a `const`
| ^^^^^^^^^ constructing invalid value at .<deref>.y.<enum-variant(B)>.0: encountered `UnsafeCell` in read-only memory
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 4, align: 4) {
╾ALLOC0╼ │ ╾──╼
}

note: erroneous constant encountered
--> $DIR/invalid-union.rs:37:25
--> $DIR/interior-mut-const-via-union.rs:37:25
|
LL | let _: &'static _ = &C;
| ^^

note: erroneous constant encountered
--> $DIR/invalid-union.rs:37:25
--> $DIR/interior-mut-const-via-union.rs:37:25
|
LL | let _: &'static _ = &C;
| ^^
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
error[E0080]: it is undefined behavior to use this value
--> $DIR/invalid-union.rs:35:1
--> $DIR/interior-mut-const-via-union.rs:35:1
|
LL | fn main() {
| ^^^^^^^^^ constructing invalid value at .<deref>.y.<enum-variant(B)>.0: encountered `UnsafeCell` in a `const`
| ^^^^^^^^^ constructing invalid value at .<deref>.y.<enum-variant(B)>.0: encountered `UnsafeCell` in read-only memory
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 8) {
╾ALLOC0╼ │ ╾──────╼
}

note: erroneous constant encountered
--> $DIR/invalid-union.rs:37:25
--> $DIR/interior-mut-const-via-union.rs:37:25
|
LL | let _: &'static _ = &C;
| ^^

note: erroneous constant encountered
--> $DIR/invalid-union.rs:37:25
--> $DIR/interior-mut-const-via-union.rs:37:25
|
LL | let _: &'static _ = &C;
| ^^
File renamed without changes.
4 changes: 2 additions & 2 deletions tests/ui/consts/min_const_fn/address_of.rs
Original file line number Diff line number Diff line change
@@ -2,15 +2,15 @@

const fn mutable_address_of_in_const() {
let mut a = 0;
let b = &raw mut a; //~ ERROR mutable reference
let b = &raw mut a; //~ ERROR mutable pointer
}

struct X;

impl X {
const fn inherent_mutable_address_of_in_const() {
let mut a = 0;
let b = &raw mut a; //~ ERROR mutable reference
let b = &raw mut a; //~ ERROR mutable pointer
}
}

4 changes: 2 additions & 2 deletions tests/ui/consts/min_const_fn/address_of.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0658]: raw mutable references are not allowed in constant functions
error[E0658]: raw mutable pointers are not allowed in constant functions
--> $DIR/address_of.rs:5:13
|
LL | let b = &raw mut a;
@@ -7,7 +7,7 @@ LL | let b = &raw mut a;
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable

error[E0658]: raw mutable references are not allowed in constant functions
error[E0658]: raw mutable pointers are not allowed in constant functions
--> $DIR/address_of.rs:13:17
|
LL | let b = &raw mut a;
Original file line number Diff line number Diff line change
@@ -38,6 +38,17 @@ LL | const READ_IMMUT: &usize = {
╾ALLOC1<imm>╼ │ ╾──╼
}

error[E0080]: it is undefined behavior to use this value
--> $DIR/const_refers_to_static.rs:34:1
|
LL | const REF_IMMUT: &u8 = &MY_STATIC;
| ^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 4, align: 4) {
╾ALLOC2<imm>╼ │ ╾──╼
}

warning: skipping const checks
|
help: skipping check that does not even have a feature gate
@@ -75,7 +86,12 @@ help: skipping check that does not even have a feature gate
|
LL | &FOO
| ^^^
help: skipping check that does not even have a feature gate
--> $DIR/const_refers_to_static.rs:34:25
|
LL | const REF_IMMUT: &u8 = &MY_STATIC;
| ^^^^^^^^^

error: aborting due to 5 previous errors; 1 warning emitted
error: aborting due to 6 previous errors; 1 warning emitted

For more information about this error, try `rustc --explain E0080`.
Original file line number Diff line number Diff line change
@@ -38,6 +38,17 @@ LL | const READ_IMMUT: &usize = {
╾ALLOC1<imm>╼ │ ╾──────╼
}

error[E0080]: it is undefined behavior to use this value
--> $DIR/const_refers_to_static.rs:34:1
|
LL | const REF_IMMUT: &u8 = &MY_STATIC;
| ^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 8) {
╾ALLOC2<imm>╼ │ ╾──────╼
}

warning: skipping const checks
|
help: skipping check that does not even have a feature gate
@@ -75,7 +86,12 @@ help: skipping check that does not even have a feature gate
|
LL | &FOO
| ^^^
help: skipping check that does not even have a feature gate
--> $DIR/const_refers_to_static.rs:34:25
|
LL | const REF_IMMUT: &u8 = &MY_STATIC;
| ^^^^^^^^^

error: aborting due to 5 previous errors; 1 warning emitted
error: aborting due to 6 previous errors; 1 warning emitted

For more information about this error, try `rustc --explain E0080`.
5 changes: 5 additions & 0 deletions tests/ui/consts/miri_unleashed/const_refers_to_static.rs
Original file line number Diff line number Diff line change
@@ -30,4 +30,9 @@ const READ_IMMUT: &usize = { //~ ERROR it is undefined behavior to use this valu
&FOO
};

static MY_STATIC: u8 = 4;
const REF_IMMUT: &u8 = &MY_STATIC;
//~^ ERROR it is undefined behavior to use this value
//~| encountered a reference pointing to a static variable

fn main() {}
155 changes: 139 additions & 16 deletions tests/ui/consts/miri_unleashed/mutable_references_err.32bit.stderr
Original file line number Diff line number Diff line change
@@ -1,54 +1,177 @@
error[E0080]: it is undefined behavior to use this value
--> $DIR/mutable_references_err.rs:15:1
error: encountered mutable pointer in final value of constant
--> $DIR/mutable_references_err.rs:17:1
|
LL | const MUH: Meh = Meh {
| ^^^^^^^^^^^^^^ constructing invalid value at .x.<deref>: encountered `UnsafeCell` in a `const`
| ^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of constant
--> $DIR/mutable_references_err.rs:27:1
|
LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
| ^^^^^^^^^^^^^^^^^^^^^^^

error[E0080]: it is undefined behavior to use this value
--> $DIR/mutable_references_err.rs:32:1
|
LL | const SUBTLE: &mut i32 = unsafe { &mut FOO };
| ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference in a `const` or `static`
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 4, align: 4) {
╾ALLOC0╼ │ ╾──╼
}

error: encountered mutable pointer in final value of constant
--> $DIR/mutable_references_err.rs:35:1
|
LL | const BLUNT: &mut i32 = &mut 42;
| ^^^^^^^^^^^^^^^^^^^^^

error[E0080]: it is undefined behavior to use this value
--> $DIR/mutable_references_err.rs:25:1
--> $DIR/mutable_references_err.rs:40:1
|
LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
| ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>.<dyn-downcast>.x: encountered `UnsafeCell` in a `const`
LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
╾ALLOC1╼ ╾ALLOC2╼ │ ╾──╼╾──╼
= note: the raw bytes of the constant (size: 4, align: 4) {
╾ALLOC1<imm>╼ ╾──╼
}

error[E0080]: it is undefined behavior to use this value
--> $DIR/mutable_references_err.rs:29:1
--> $DIR/mutable_references_err.rs:47:1
|
LL | const BLUNT: &mut i32 = &mut 42;
| ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference in a `const`
LL | const POINTS_TO_MUTABLE1: &i32 = unsafe { &MUTABLE };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 4, align: 4) {
ALLOC3╼ │ ╾──╼
ALLOC2<imm>╼ │ ╾──╼
}

error[E0080]: evaluation of constant value failed
--> $DIR/mutable_references_err.rs:51:43
|
LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF };
| ^^^^^^^^^^^^^ constant accesses static

error: encountered mutable pointer in final value of constant
--> $DIR/mutable_references_err.rs:55:1
|
LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of constant
--> $DIR/mutable_references_err.rs:65:1
|
LL | const RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of constant
--> $DIR/mutable_references_err.rs:67:1
|
LL | const RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of constant
--> $DIR/mutable_references_err.rs:69:1
|
LL | const RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: skipping const checks
|
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:16:8
--> $DIR/mutable_references_err.rs:18:8
|
LL | x: &UnsafeCell::new(42),
| ^^^^^^^^^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:25:27
--> $DIR/mutable_references_err.rs:27:27
|
LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:29:25
--> $DIR/mutable_references_err.rs:32:40
|
LL | const SUBTLE: &mut i32 = unsafe { &mut FOO };
| ^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:32:40
|
LL | const SUBTLE: &mut i32 = unsafe { &mut FOO };
| ^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:32:35
|
LL | const SUBTLE: &mut i32 = unsafe { &mut FOO };
| ^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:35:25
|
LL | const BLUNT: &mut i32 = &mut 42;
| ^^^^^^^
help: skipping check for `const_mut_refs` feature
--> $DIR/mutable_references_err.rs:40:49
|
LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: skipping check for `const_mut_refs` feature
--> $DIR/mutable_references_err.rs:40:49
|
LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:40:49
|
LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:47:44
|
LL | const POINTS_TO_MUTABLE1: &i32 = unsafe { &MUTABLE };
| ^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:47:44
|
LL | const POINTS_TO_MUTABLE1: &i32 = unsafe { &MUTABLE };
| ^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:50:36
|
LL | static mut MUTABLE_REF: &mut i32 = &mut 42;
| ^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:51:45
|
LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF };
| ^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:51:45
|
LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF };
| ^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:55:45
|
LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _;
| ^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:65:51
|
LL | const RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
| ^^^^^^^^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:67:50
|
LL | const RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
| ^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:69:51
|
LL | const RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
| ^^^^^^

error: aborting due to 3 previous errors; 1 warning emitted
error: aborting due to 11 previous errors; 1 warning emitted

For more information about this error, try `rustc --explain E0080`.
155 changes: 139 additions & 16 deletions tests/ui/consts/miri_unleashed/mutable_references_err.64bit.stderr
Original file line number Diff line number Diff line change
@@ -1,54 +1,177 @@
error[E0080]: it is undefined behavior to use this value
--> $DIR/mutable_references_err.rs:15:1
error: encountered mutable pointer in final value of constant
--> $DIR/mutable_references_err.rs:17:1
|
LL | const MUH: Meh = Meh {
| ^^^^^^^^^^^^^^ constructing invalid value at .x.<deref>: encountered `UnsafeCell` in a `const`
| ^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of constant
--> $DIR/mutable_references_err.rs:27:1
|
LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
| ^^^^^^^^^^^^^^^^^^^^^^^

error[E0080]: it is undefined behavior to use this value
--> $DIR/mutable_references_err.rs:32:1
|
LL | const SUBTLE: &mut i32 = unsafe { &mut FOO };
| ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference in a `const` or `static`
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 8) {
╾ALLOC0╼ │ ╾──────╼
}

error: encountered mutable pointer in final value of constant
--> $DIR/mutable_references_err.rs:35:1
|
LL | const BLUNT: &mut i32 = &mut 42;
| ^^^^^^^^^^^^^^^^^^^^^

error[E0080]: it is undefined behavior to use this value
--> $DIR/mutable_references_err.rs:25:1
--> $DIR/mutable_references_err.rs:40:1
|
LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
| ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>.<dyn-downcast>.x: encountered `UnsafeCell` in a `const`
LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 16, align: 8) {
╾ALLOC1╼ ╾ALLOC2╼ │ ╾──────╼╾──────╼
= note: the raw bytes of the constant (size: 8, align: 8) {
╾ALLOC1<imm>╼ │ ╾──────╼
}

error[E0080]: it is undefined behavior to use this value
--> $DIR/mutable_references_err.rs:29:1
--> $DIR/mutable_references_err.rs:47:1
|
LL | const BLUNT: &mut i32 = &mut 42;
| ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference in a `const`
LL | const POINTS_TO_MUTABLE1: &i32 = unsafe { &MUTABLE };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 8) {
ALLOC3╼ │ ╾──────╼
ALLOC2<imm>╼ │ ╾──────╼
}

error[E0080]: evaluation of constant value failed
--> $DIR/mutable_references_err.rs:51:43
|
LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF };
| ^^^^^^^^^^^^^ constant accesses static

error: encountered mutable pointer in final value of constant
--> $DIR/mutable_references_err.rs:55:1
|
LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of constant
--> $DIR/mutable_references_err.rs:65:1
|
LL | const RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of constant
--> $DIR/mutable_references_err.rs:67:1
|
LL | const RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of constant
--> $DIR/mutable_references_err.rs:69:1
|
LL | const RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: skipping const checks
|
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:16:8
--> $DIR/mutable_references_err.rs:18:8
|
LL | x: &UnsafeCell::new(42),
| ^^^^^^^^^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:25:27
--> $DIR/mutable_references_err.rs:27:27
|
LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:29:25
--> $DIR/mutable_references_err.rs:32:40
|
LL | const SUBTLE: &mut i32 = unsafe { &mut FOO };
| ^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:32:40
|
LL | const SUBTLE: &mut i32 = unsafe { &mut FOO };
| ^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:32:35
|
LL | const SUBTLE: &mut i32 = unsafe { &mut FOO };
| ^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:35:25
|
LL | const BLUNT: &mut i32 = &mut 42;
| ^^^^^^^
help: skipping check for `const_mut_refs` feature
--> $DIR/mutable_references_err.rs:40:49
|
LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: skipping check for `const_mut_refs` feature
--> $DIR/mutable_references_err.rs:40:49
|
LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:40:49
|
LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:47:44
|
LL | const POINTS_TO_MUTABLE1: &i32 = unsafe { &MUTABLE };
| ^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:47:44
|
LL | const POINTS_TO_MUTABLE1: &i32 = unsafe { &MUTABLE };
| ^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:50:36
|
LL | static mut MUTABLE_REF: &mut i32 = &mut 42;
| ^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:51:45
|
LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF };
| ^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:51:45
|
LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF };
| ^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:55:45
|
LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _;
| ^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:65:51
|
LL | const RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
| ^^^^^^^^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:67:50
|
LL | const RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
| ^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:69:51
|
LL | const RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
| ^^^^^^

error: aborting due to 3 previous errors; 1 warning emitted
error: aborting due to 11 previous errors; 1 warning emitted

For more information about this error, try `rustc --explain E0080`.
46 changes: 43 additions & 3 deletions tests/ui/consts/miri_unleashed/mutable_references_err.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// stderr-per-bitwidth
// compile-flags: -Zunleash-the-miri-inside-of-you
#![allow(invalid_reference_casting)]

use std::sync::atomic::*;
use std::cell::UnsafeCell;

// this test ensures that our mutability story is sound
@@ -12,7 +14,7 @@ unsafe impl Sync for Meh {}

// the following will never be ok! no interior mut behind consts, because
// all allocs interned here will be marked immutable.
const MUH: Meh = Meh { //~ ERROR: it is undefined behavior to use this value
const MUH: Meh = Meh { //~ ERROR: mutable pointer in final value
x: &UnsafeCell::new(42),
};

@@ -23,11 +25,49 @@ unsafe impl Sync for Synced {}

// Make sure we also catch this behind a type-erased `dyn Trait` reference.
const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
//~^ ERROR: it is undefined behavior to use this value
//~^ ERROR: mutable pointer in final value

// Make sure we also catch mutable references.
// Make sure we also catch mutable references in values that shouldn't have them.
static mut FOO: i32 = 0;
const SUBTLE: &mut i32 = unsafe { &mut FOO };
//~^ ERROR: it is undefined behavior to use this value
//~| static
const BLUNT: &mut i32 = &mut 42;
//~^ ERROR: mutable pointer in final value

// Check for mutable references to read-only memory.
static READONLY: i32 = 0;
static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) };
//~^ ERROR: it is undefined behavior to use this value
//~| pointing to read-only memory

// Check for consts pointing to mutable memory.
// Currently it's not even possible to create such a const.
static mut MUTABLE: i32 = 42;
const POINTS_TO_MUTABLE1: &i32 = unsafe { &MUTABLE };
//~^ ERROR: undefined behavior to use this value
//~| pointing to a static
static mut MUTABLE_REF: &mut i32 = &mut 42;
const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF };
//~^ ERROR: evaluation of constant value failed
//~| accesses static

const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _;
//~^ ERROR: mutable pointer in final value

struct SyncPtr<T> { x : *const T }
unsafe impl<T> Sync for SyncPtr<T> {}

// These pass the lifetime checks because of the "tail expression" / "outer scope" rule.
// (This relies on `SyncPtr` being a curly brace struct.)
// However, we intern the inner memory as read-only, so this must be rejected.
// (Also see `static-no-inner-mut` for similar tests on `static`.)
const RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
//~^ ERROR mutable pointer in final value
const RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
//~^ ERROR mutable pointer in final value
const RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
//~^ ERROR mutable pointer in final value

fn main() {
unsafe {
3 changes: 2 additions & 1 deletion tests/ui/consts/miri_unleashed/raw_mutable_const.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// compile-flags: -Zunleash-the-miri-inside-of-you
// Similar to `raw-ptr-const.rs`, but with *mutable* data. *Must* be rejected.

use std::cell::UnsafeCell;

const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
//~^ ERROR: unsupported untyped pointer in constant
//~^ ERROR: mutable pointer in final value

fn main() {}
8 changes: 3 additions & 5 deletions tests/ui/consts/miri_unleashed/raw_mutable_const.stderr
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
error: unsupported untyped pointer in constant
--> $DIR/raw_mutable_const.rs:5:1
error: encountered mutable pointer in final value of constant
--> $DIR/raw_mutable_const.rs:6:1
|
LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: memory only reachable via raw pointers is not supported

warning: skipping const checks
|
help: skipping check that does not even have a feature gate
--> $DIR/raw_mutable_const.rs:5:38
--> $DIR/raw_mutable_const.rs:6:38
|
LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
| ^^^^^^^^^^^^^^^^^^^^
82 changes: 82 additions & 0 deletions tests/ui/consts/miri_unleashed/static-no-inner-mut.32bit.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
error: encountered mutable pointer in final value of static
--> $DIR/static-no-inner-mut.rs:9:1
|
LL | static REF: &AtomicI32 = &AtomicI32::new(42);
| ^^^^^^^^^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of static
--> $DIR/static-no-inner-mut.rs:10:1
|
LL | static REFMUT: &mut i32 = &mut 0;
| ^^^^^^^^^^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of static
--> $DIR/static-no-inner-mut.rs:13:1
|
LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}};
| ^^^^^^^^^^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of static
--> $DIR/static-no-inner-mut.rs:14:1
|
LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
| ^^^^^^^^^^^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of static
--> $DIR/static-no-inner-mut.rs:29:1
|
LL | static RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of static
--> $DIR/static-no-inner-mut.rs:31:1
|
LL | static RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of static
--> $DIR/static-no-inner-mut.rs:33:1
|
LL | static RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: skipping const checks
|
help: skipping check that does not even have a feature gate
--> $DIR/static-no-inner-mut.rs:9:26
|
LL | static REF: &AtomicI32 = &AtomicI32::new(42);
| ^^^^^^^^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/static-no-inner-mut.rs:10:27
|
LL | static REFMUT: &mut i32 = &mut 0;
| ^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/static-no-inner-mut.rs:13:56
|
LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}};
| ^^^^
help: skipping check that does not even have a feature gate
--> $DIR/static-no-inner-mut.rs:14:44
|
LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
| ^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/static-no-inner-mut.rs:29:52
|
LL | static RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
| ^^^^^^^^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/static-no-inner-mut.rs:31:51
|
LL | static RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
| ^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/static-no-inner-mut.rs:33:52
|
LL | static RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
| ^^^^^^

error: aborting due to 7 previous errors; 1 warning emitted

82 changes: 82 additions & 0 deletions tests/ui/consts/miri_unleashed/static-no-inner-mut.64bit.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
error: encountered mutable pointer in final value of static
--> $DIR/static-no-inner-mut.rs:9:1
|
LL | static REF: &AtomicI32 = &AtomicI32::new(42);
| ^^^^^^^^^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of static
--> $DIR/static-no-inner-mut.rs:10:1
|
LL | static REFMUT: &mut i32 = &mut 0;
| ^^^^^^^^^^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of static
--> $DIR/static-no-inner-mut.rs:13:1
|
LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}};
| ^^^^^^^^^^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of static
--> $DIR/static-no-inner-mut.rs:14:1
|
LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
| ^^^^^^^^^^^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of static
--> $DIR/static-no-inner-mut.rs:29:1
|
LL | static RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of static
--> $DIR/static-no-inner-mut.rs:31:1
|
LL | static RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: encountered mutable pointer in final value of static
--> $DIR/static-no-inner-mut.rs:33:1
|
LL | static RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: skipping const checks
|
help: skipping check that does not even have a feature gate
--> $DIR/static-no-inner-mut.rs:9:26
|
LL | static REF: &AtomicI32 = &AtomicI32::new(42);
| ^^^^^^^^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/static-no-inner-mut.rs:10:27
|
LL | static REFMUT: &mut i32 = &mut 0;
| ^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/static-no-inner-mut.rs:13:56
|
LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}};
| ^^^^
help: skipping check that does not even have a feature gate
--> $DIR/static-no-inner-mut.rs:14:44
|
LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
| ^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/static-no-inner-mut.rs:29:52
|
LL | static RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
| ^^^^^^^^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/static-no-inner-mut.rs:31:51
|
LL | static RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
| ^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/static-no-inner-mut.rs:33:52
|
LL | static RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
| ^^^^^^

error: aborting due to 7 previous errors; 1 warning emitted

36 changes: 36 additions & 0 deletions tests/ui/consts/miri_unleashed/static-no-inner-mut.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// stderr-per-bitwidth
// compile-flags: -Zunleash-the-miri-inside-of-you
#![feature(const_refs_to_cell, const_mut_refs)]
// All "inner" allocations that come with a `static` are interned immutably. This means it is
// crucial that we do not accept any form of (interior) mutability there.

use std::sync::atomic::*;

static REF: &AtomicI32 = &AtomicI32::new(42); //~ERROR mutable pointer in final value
static REFMUT: &mut i32 = &mut 0; //~ERROR mutable pointer in final value

// Different way of writing this that avoids promotion.
static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}}; //~ERROR mutable pointer in final value
static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}}; //~ERROR mutable pointer in final value

// This one is obvious, since it is non-Sync. (It also suppresses the other errors, so it is
// commented out.)
// static RAW: *const AtomicI32 = &AtomicI32::new(42);

struct SyncPtr<T> { x : *const T }
unsafe impl<T> Sync for SyncPtr<T> {}

// All of these pass the lifetime checks because of the "tail expression" / "outer scope" rule.
// (This relies on `SyncPtr` being a curly brace struct.)
// Then they get interned immutably, which is not great.
// `mut_ref_in_final.rs` and `std/cell.rs` ensure that we don't accept this even with the feature
// fate, but for unleashed Miri there's not really any way we can reject them: it's just
// non-dangling raw pointers.
static RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
//~^ ERROR mutable pointer in final value
static RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
//~^ ERROR mutable pointer in final value
static RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
//~^ ERROR mutable pointer in final value

fn main() {}
6 changes: 3 additions & 3 deletions tests/ui/consts/raw-ptr-const.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// check-pass

// This is a regression test for a `span_delayed_bug` during interning when a constant
// evaluates to a (non-dangling) raw pointer. For now this errors; potentially it
// could also be allowed.
// evaluates to a (non-dangling) raw pointer.

const CONST_RAW: *const Vec<i32> = &Vec::new() as *const _;
//~^ ERROR unsupported untyped pointer in constant

fn main() {}
10 changes: 0 additions & 10 deletions tests/ui/consts/raw-ptr-const.stderr

This file was deleted.

18 changes: 18 additions & 0 deletions tests/ui/consts/refs-to-cell-in-final.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#![feature(const_refs_to_cell)]

use std::cell::*;

struct SyncPtr<T> { x : *const T }
unsafe impl<T> Sync for SyncPtr<T> {}

// These pass the lifetime checks because of the "tail expression" / "outer scope" rule.
// (This relies on `SyncPtr` being a curly brace struct.)
// However, we intern the inner memory as read-only.
// The resulting constant would pass all validation checks, so it is crucial that this gets rejected
// by static const checks!
static RAW_SYNC_S: SyncPtr<Cell<i32>> = SyncPtr { x: &Cell::new(42) };
//~^ ERROR: cannot refer to interior mutable data
const RAW_SYNC_C: SyncPtr<Cell<i32>> = SyncPtr { x: &Cell::new(42) };
//~^ ERROR: cannot refer to interior mutable data

fn main() {}
17 changes: 17 additions & 0 deletions tests/ui/consts/refs-to-cell-in-final.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0492]: statics cannot refer to interior mutable data
--> $DIR/refs-to-cell-in-final.rs:13:54
|
LL | static RAW_SYNC_S: SyncPtr<Cell<i32>> = SyncPtr { x: &Cell::new(42) };
| ^^^^^^^^^^^^^^ 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

error[E0492]: constants cannot refer to interior mutable data
--> $DIR/refs-to-cell-in-final.rs:15:53
|
LL | const RAW_SYNC_C: SyncPtr<Cell<i32>> = SyncPtr { x: &Cell::new(42) };
| ^^^^^^^^^^^^^^ this borrow of an interior mutable value may end up in the final value

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0492`.
8 changes: 4 additions & 4 deletions tests/ui/consts/std/cell.stderr
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
error: encountered dangling pointer in final constant
error: encountered dangling pointer in final value of static
--> $DIR/cell.rs:6:1
|
LL | static FOO: Wrap<*mut u32> = Wrap(Cell::new(42).as_ptr());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^

error: encountered dangling pointer in final constant
error: encountered dangling pointer in final value of constant
--> $DIR/cell.rs:8:1
|
LL | const FOO_CONST: Wrap<*mut u32> = Wrap(Cell::new(42).as_ptr());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: encountered dangling pointer in final constant
error: encountered dangling pointer in final value of constant
--> $DIR/cell.rs:22:1
|
LL | const FOO4_CONST: Wrap<*mut u32> = Wrap(FOO3_CONST.0.as_ptr());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: encountered dangling pointer in final constant
error: encountered dangling pointer in final value of constant
--> $DIR/cell.rs:27:1
|
LL | const FOO2: *mut u32 = Cell::new(42).as_ptr();