use syntax::ast::Mutability;
-use super::{
- AllocId, Allocation, InterpCx, Machine, MemoryKind, MPlaceTy, Scalar, ValueVisitor,
-};
+use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, Scalar, ValueVisitor};
-pub trait CompileTimeMachine<'mir, 'tcx> =
- Machine<
- 'mir,
- 'tcx,
- MemoryKinds = !,
- PointerTag = (),
- ExtraFnVal = !,
- FrameExtra = (),
- MemoryExtra = (),
- AllocExtra = (),
- MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation)>,
- >;
+pub trait CompileTimeMachine<'mir, 'tcx> = Machine<
+ 'mir,
+ 'tcx,
+ MemoryKinds = !,
+ PointerTag = (),
+ ExtraFnVal = !,
+ FrameExtra = (),
+ AllocExtra = (),
+ MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation)>,
+>;
struct InternVisitor<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> {
/// The ectx from which we intern.
// treat dangling pointers like other statics
// just to stop trying to recurse into them
return Ok(Some(IsStaticOrFn));
- },
+ }
};
// This match is just a canary for future changes to `MemoryKind`, which most likely need
// changes in this function.
match kind {
- MemoryKind::Stack | MemoryKind::Vtable | MemoryKind::CallerLocation => {},
+ MemoryKind::Stack | MemoryKind::Vtable | MemoryKind::CallerLocation => {}
}
// Set allocation mutability as appropriate. This is used by LLVM to put things into
// read-only memory, and also by Miri when evluating other constants/statics that
// access this one.
if mode == InternMode::Static {
// When `ty` is `None`, we assume no interior mutability.
- let frozen = ty.map_or(true, |ty| ty.is_freeze(
- ecx.tcx.tcx,
- ecx.param_env,
- ecx.tcx.span,
- ));
+ let frozen = ty.map_or(true, |ty| ty.is_freeze(ecx.tcx.tcx, ecx.param_env, ecx.tcx.span));
// For statics, allocation mutability is the combination of the place mutability and
// the type mutability.
// The entire allocation needs to be mutable if it contains an `UnsafeCell` anywhere.
// initial value was computed.
// Constants are never mutable.
assert_eq!(
- mutability, Mutability::Not,
+ mutability,
+ Mutability::Not,
"Something went very wrong: mutability requested for a constant"
);
alloc.mutability = Mutability::Not;
mutability: Mutability,
ty: Option<Ty<'tcx>>,
) -> InterpResult<'tcx, Option<IsStaticOrFn>> {
- intern_shallow(
- self.ecx,
- self.leftover_allocations,
- self.mode,
- alloc_id,
- mutability,
- ty,
- )
+ intern_shallow(self.ecx, self.leftover_allocations, self.mode, alloc_id, mutability, ty)
}
}
-impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>
- ValueVisitor<'mir, 'tcx, M>
-for
- InternVisitor<'rt, 'mir, 'tcx, M>
+impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
+ for InternVisitor<'rt, 'mir, 'tcx, M>
{
type V = MPlaceTy<'tcx>;
fn visit_aggregate(
&mut self,
mplace: MPlaceTy<'tcx>,
- fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>,
+ fields: impl Iterator<Item = InterpResult<'tcx, Self::V>>,
) -> InterpResult<'tcx> {
if let Some(def) = mplace.layout.ty.ty_adt_def() {
if Some(def.did) == self.ecx.tcx.lang_items().unsafe_cell_type() {
// allocations.
let old = std::mem::replace(&mut self.mutability, Mutability::Mut);
assert_ne!(
- self.mode, InternMode::Const,
+ self.mode,
+ InternMode::Const,
"UnsafeCells are not allowed behind references in constants. This should have \
been prevented statically by const qualification. If this were allowed one \
would be able to change a constant at one use site and other use sites could \
let mplace = self.ecx.ref_to_mplace(value)?;
// Handle trait object vtables
if let ty::Dynamic(..) =
- self.ecx.tcx.struct_tail_erasing_lifetimes(
- referenced_ty, self.ecx.param_env).kind
+ self.ecx.tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind
{
- if let Ok(vtable) = mplace.meta.unwrap().to_ptr() {
+ // Validation has already errored on an invalid vtable pointer so we can safely not
+ // do anything if this is not a real pointer
+ if let Scalar::Ptr(vtable) = mplace.meta.unwrap() {
// explitly choose `Immutable` here, since vtables are immutable, even
// if the reference of the fat pointer is mutable
self.intern_shallow(vtable.alloc_id, Mutability::Not, None)?;
+ } else {
+ self.ecx().tcx.sess.delay_span_bug(
+ syntax_pos::DUMMY_SP,
+ "vtables pointers cannot be integer pointers",
+ );
}
}
// Check if we have encountered this pointer+layout combination before.
// const qualification enforces it. We can lift it in the future.
match (self.mode, mutability) {
// immutable references are fine everywhere
- (_, hir::Mutability::Not) => {},
+ (_, hir::Mutability::Not) => {}
// all is "good and well" in the unsoundness of `static mut`
// mutable references are ok in `static`. Either they are treated as immutable
// because they are behind an immutable one, or they are behind an `UnsafeCell`
// and thus ok.
- (InternMode::Static, hir::Mutability::Mut) => {},
+ (InternMode::Static, hir::Mutability::Mut) => {}
// we statically prevent `&mut T` via `const_qualif` and double check this here
- (InternMode::ConstBase, hir::Mutability::Mut) |
- (InternMode::Const, hir::Mutability::Mut) => {
- match referenced_ty.kind {
- ty::Array(_, n)
- if n.eval_usize(self.ecx.tcx.tcx, self.ecx.param_env) == 0 => {}
- ty::Slice(_)
- if mplace.meta.unwrap().to_machine_usize(self.ecx)? == 0 => {}
- _ => bug!("const qualif failed to prevent mutable references"),
- }
+ (InternMode::ConstBase, hir::Mutability::Mut)
+ | (InternMode::Const, hir::Mutability::Mut) => match referenced_ty.kind {
+ ty::Array(_, n)
+ if n.eval_usize(self.ecx.tcx.tcx, self.ecx.param_env) == 0 => {}
+ ty::Slice(_) if mplace.meta.unwrap().to_machine_usize(self.ecx)? == 0 => {}
+ _ => bug!("const qualif failed to prevent mutable references"),
},
}
// Compute the mutability with which we'll start visiting the allocation. This is
match self.intern_shallow(ptr.alloc_id, mutability, Some(mplace.layout.ty))? {
// No need to recurse, these are interned already and statics may have
// cycles, so we don't want to recurse there
- Some(IsStaticOrFn) => {},
+ Some(IsStaticOrFn) => {}
// intern everything referenced by this value. The mutability is taken from the
// reference. It is checked above that mutable references only happen in
// `static mut`
ecx,
leftover_allocations,
base_intern_mode,
- ret.ptr.to_ptr()?.alloc_id,
+ // The outermost allocation must exist, because we allocated it with
+ // `Memory::allocate`.
+ ret.ptr.assert_ptr().alloc_id,
base_mutability,
- Some(ret.layout.ty)
+ Some(ret.layout.ty),
)?;
while let Some(((mplace, mutability, mode), _)) = ref_tracking.todo.pop() {
mode,
leftover_allocations,
mutability,
- }.visit_value(mplace);
+ }
+ .visit_value(mplace);
if let Err(error) = interned {
// This can happen when e.g. the tag of an enum is not a valid discriminant. We do have
// to read enum discriminants in order to find references in enum variant fields.
diag.note(crate::const_eval::note_on_undefined_behavior_error());
diag.emit();
}
- Err(ErrorHandled::TooGeneric) |
- Err(ErrorHandled::Reported) => {},
+ Err(ErrorHandled::TooGeneric) | Err(ErrorHandled::Reported) => {}
}
}
}
// 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.
- if base_intern_mode != InternMode::Static {
- // If it's not a static, it *must* be immutable.
- // We cannot have mutable memory inside a constant.
- // FIXME: ideally we would assert that they already are immutable, to double-
- // check our static checks.
- alloc.mutability = Mutability::Not;
+ match base_intern_mode {
+ InternMode::Static => {}
+ InternMode::Const | InternMode::ConstBase => {
+ // If it's not a static, 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.
+ if alloc.mutability == Mutability::Mut {
+ // For better errors later, mark the allocation as immutable
+ // (on top of the delayed ICE).
+ alloc.mutability = Mutability::Not;
+ ecx.tcx.sess.delay_span_bug(ecx.tcx.span, "mutable allocation in constant");
+ }
+ }
}
let alloc = tcx.intern_const_alloc(alloc);
tcx.alloc_map.lock().set_alloc_id_memory(alloc_id, alloc);
} else if ecx.memory.dead_alloc_map.contains_key(&alloc_id) {
// dangling pointer
throw_unsup!(ValidationFailure("encountered dangling pointer in final constant".into()))
+ } else if ecx.tcx.alloc_map.lock().get(alloc_id).is_none() {
+ span_bug!(ecx.tcx.span, "encountered unknown alloc id {:?}", alloc_id);
}
}
Ok(())