diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index aa7be3d80e1..aad0e162935 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -5,7 +5,7 @@ use rustc::ty::layout::VariantIdx; use rustc::ty::{self, TyCtxt}; use rustc_span::{source_map::DUMMY_SP, symbol::Symbol}; -use crate::interpret::{intern_const_alloc_recursive, ConstValue, InterpCx}; +use crate::interpret::{intern_const_alloc_recursive, ConstValue, InternKind, InterpCx}; mod error; mod eval_queries; @@ -52,7 +52,7 @@ pub(crate) fn const_caller_location<'tcx>( let loc_ty = tcx.caller_location_ty(); let loc_place = ecx.alloc_caller_location(file, line, col); - intern_const_alloc_recursive(&mut ecx, None, loc_place, false).unwrap(); + intern_const_alloc_recursive(&mut ecx, InternKind::Constant, loc_place, false).unwrap(); let loc_const = ty::Const { ty: loc_ty, val: ty::ConstKind::Value(ConstValue::Scalar(loc_place.ptr.into())), diff --git a/src/librustc_mir/const_eval/eval_queries.rs b/src/librustc_mir/const_eval/eval_queries.rs index d260a6808d1..442baf85f2b 100644 --- a/src/librustc_mir/const_eval/eval_queries.rs +++ b/src/librustc_mir/const_eval/eval_queries.rs @@ -1,9 +1,9 @@ use super::{error_to_const_error, CompileTimeEvalContext, CompileTimeInterpreter, MemoryExtra}; use crate::interpret::eval_nullary_intrinsic; use crate::interpret::{ - intern_const_alloc_recursive, Allocation, ConstValue, GlobalId, ImmTy, Immediate, InterpCx, - InterpResult, MPlaceTy, MemoryKind, OpTy, RawConst, RefTracking, Scalar, ScalarMaybeUndef, - StackPopCleanup, + intern_const_alloc_recursive, Allocation, ConstValue, GlobalId, ImmTy, Immediate, InternKind, + InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RawConst, RefTracking, Scalar, + ScalarMaybeUndef, StackPopCleanup, }; use rustc::mir; use rustc::mir::interpret::{ConstEvalErr, ErrorHandled}; @@ -56,9 +56,14 @@ fn eval_body_using_ecx<'mir, 'tcx>( ecx.run()?; // Intern the result + let intern_kind = match tcx.static_mutability(cid.instance.def_id()) { + Some(m) => InternKind::Static(m), + None if cid.promoted.is_some() => InternKind::Promoted, + _ => InternKind::Constant, + }; intern_const_alloc_recursive( ecx, - tcx.static_mutability(cid.instance.def_id()), + intern_kind, ret, body.ignore_interior_mut_in_const_validation, )?; diff --git a/src/librustc_mir/interpret/intern.rs b/src/librustc_mir/interpret/intern.rs index 220761ce28d..0c65b77a382 100644 --- a/src/librustc_mir/interpret/intern.rs +++ b/src/librustc_mir/interpret/intern.rs @@ -268,19 +268,27 @@ impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx } } +pub enum InternKind { + /// The `mutability` of the static, ignoring the type which may have interior mutability. + Static(hir::Mutability), + Constant, + Promoted, + ConstProp, +} + pub fn intern_const_alloc_recursive>( ecx: &mut InterpCx<'mir, 'tcx, M>, - // The `mutability` of the place, ignoring the type. - place_mut: Option, + intern_kind: InternKind, ret: MPlaceTy<'tcx>, ignore_interior_mut_in_const_validation: bool, ) -> InterpResult<'tcx> { let tcx = ecx.tcx; - let (base_mutability, base_intern_mode) = match place_mut { + let (base_mutability, base_intern_mode) = match intern_kind { // `static mut` doesn't care about interior mutability, it's mutable anyway - Some(mutbl) => (mutbl, InternMode::Static), - // consts, promoteds. FIXME: what about array lengths, array initializers? - None => (Mutability::Not, InternMode::ConstBase), + InternKind::Static(mutbl) => (mutbl, InternMode::Static), + // FIXME: what about array lengths, array initializers? + InternKind::Constant | InternKind::ConstProp => (Mutability::Not, InternMode::ConstBase), + InternKind::Promoted => (Mutability::Not, InternMode::ConstBase), }; // Type based interning. @@ -338,10 +346,24 @@ pub fn intern_const_alloc_recursive>( // We can't call the `intern_shallow` method here, as its logic is tailored to safe // references and a `leftover_allocations` set (where we only have a todo-list here). // So we hand-roll the interning logic here again. - match base_intern_mode { - InternMode::Static => {} - InternMode::Const | InternMode::ConstBase => { - // If it's not a static, it *must* be immutable. + match intern_kind { + // Statics may contain mutable allocations even behind relocations. + // Even for immutable statics it would be ok to have mutable allocations behind + // raw pointers, e.g. for `static FOO: *const AtomicUsize = &AtomicUsize::new(42)`. + InternKind::Static(_) => {} + // Raw pointers in promoteds may only point to immutable things so we mark + // everything as immutable. + // It is UB to mutate through a raw pointer obtained via an immutable reference. + // Since all references and pointers inside a promoted must by their very definition + // be created from an immutable reference (and promotion also excludes interior + // mutability), mutating through them would be UB. + // There's no way we can check whether the user is using raw pointers correctly, + // so all we can do is mark this as immutable here. + InternKind::Promoted => { + alloc.mutability = Mutability::Not; + } + InternKind::Constant | InternKind::ConstProp => { + // If it's a constant, it *must* be immutable. // We cannot have mutable memory inside a constant. // We use `delay_span_bug` here, because this can be reached in the presence // of fancy transmutes. @@ -364,6 +386,8 @@ pub fn intern_const_alloc_recursive>( // dangling pointer throw_unsup!(ValidationFailure("encountered dangling pointer in final constant".into())) } else if ecx.tcx.alloc_map.lock().get(alloc_id).is_none() { + // We have hit an `AllocId` that is neither in local or global memory and isn't marked + // as dangling by local memory. span_bug!(ecx.tcx.span, "encountered unknown alloc id {:?}", alloc_id); } } diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs index 2e8fbb95ca2..a519f38e712 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -32,6 +32,6 @@ pub use self::visitor::{MutValueVisitor, ValueVisitor}; pub use self::validity::RefTracking; -pub use self::intern::intern_const_alloc_recursive; +pub use self::intern::{intern_const_alloc_recursive, InternKind}; crate use self::intrinsics::eval_nullary_intrinsic; diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 90c97480c75..9d5dbe3564e 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -30,9 +30,9 @@ use syntax::ast::Mutability; use crate::const_eval::error_to_const_error; use crate::interpret::{ - self, intern_const_alloc_recursive, AllocId, Allocation, Frame, ImmTy, Immediate, InterpCx, - LocalState, LocalValue, Memory, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Pointer, - ScalarMaybeUndef, StackPopCleanup, + self, intern_const_alloc_recursive, AllocId, Allocation, Frame, ImmTy, Immediate, InternKind, + InterpCx, LocalState, LocalValue, Memory, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, + Pointer, ScalarMaybeUndef, StackPopCleanup, }; use crate::transform::{MirPass, MirSource}; @@ -767,7 +767,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { )) => l.is_bits() && r.is_bits(), interpret::Operand::Indirect(_) if mir_opt_level >= 2 => { let mplace = op.assert_mem_place(&self.ecx); - intern_const_alloc_recursive(&mut self.ecx, None, mplace, false) + intern_const_alloc_recursive(&mut self.ecx, InternKind::ConstProp, mplace, false) .expect("failed to intern alloc"); true } diff --git a/src/test/ui/consts/raw_pointer_promoted.rs b/src/test/ui/consts/raw_pointer_promoted.rs new file mode 100644 index 00000000000..4c62ad444a5 --- /dev/null +++ b/src/test/ui/consts/raw_pointer_promoted.rs @@ -0,0 +1,5 @@ +// check-pass + +pub const FOO: &'static *const i32 = &(&0 as _); + +fn main() {}