Skip to content
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

Support allocation failures when interpreting MIR #86255

Merged
merged 18 commits into from
Jul 4, 2021
Merged
Show file tree
Hide file tree
Changes from 15 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
5 changes: 4 additions & 1 deletion compiler/rustc_codegen_cranelift/src/vtable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ pub(crate) fn get_vtable<'tcx>(
let vtable_ptr = if let Some(vtable_ptr) = fx.vtables.get(&(ty, trait_ref)) {
*vtable_ptr
} else {
let vtable_alloc_id = fx.tcx.vtable_allocation(ty, trait_ref);
let vtable_alloc_id = match fx.tcx.vtable_allocation(ty, trait_ref) {
Ok(alloc) => alloc,
Err(_) => fx.tcx.sess.fatal("allocation of constant vtable failed"),
};
let vtable_allocation = fx.tcx.global_alloc(vtable_alloc_id).unwrap_memory();
let vtable_ptr = pointer_for_allocation(fx, vtable_allocation);

Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_codegen_ssa/src/meth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>(
return val;
}

let vtable_alloc_id = tcx.vtable_allocation(ty, trait_ref);
let vtable_alloc_id = match tcx.vtable_allocation(ty, trait_ref) {
Ok(alloc) => alloc,
Err(_) => tcx.sess.fatal("allocation of constant vtable failed"),
};
let vtable_allocation = tcx.global_alloc(vtable_alloc_id).unwrap_memory();
let vtable_const = cx.const_data_from_alloc(vtable_allocation);
let align = cx.data_layout().pointer_align.abi;
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#![feature(associated_type_defaults)]
#![feature(iter_zip)]
#![feature(thread_local_const_init)]
#![feature(try_reserve)]
#![recursion_limit = "512"]

#[macro_use]
Expand Down
30 changes: 24 additions & 6 deletions compiler/rustc_middle/src/mir/interpret/allocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ use std::ptr;

use rustc_ast::Mutability;
use rustc_data_structures::sorted_map::SortedMap;
use rustc_span::DUMMY_SP;
use rustc_target::abi::{Align, HasDataLayout, Size};

use super::{
read_target_uint, write_target_uint, AllocId, InterpError, Pointer, Scalar, ScalarMaybeUninit,
UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo,
read_target_uint, write_target_uint, AllocId, InterpError, InterpResult, Pointer,
ResourceExhaustionInfo, Scalar, ScalarMaybeUninit, UndefinedBehaviorInfo, UninitBytesAccess,
UnsupportedOpInfo,
};
use crate::ty;

/// This type represents an Allocation in the Miri/CTFE core engine.
///
Expand Down Expand Up @@ -121,15 +124,30 @@ impl<Tag> Allocation<Tag> {
Allocation::from_bytes(slice, Align::ONE, Mutability::Not)
}

pub fn uninit(size: Size, align: Align) -> Self {
Allocation {
bytes: vec![0; size.bytes_usize()],
/// Try to create an Allocation of `size` bytes, failing if there is not enough memory
/// available to the compiler to do so.
pub fn uninit(size: Size, align: Align) -> InterpResult<'static, Self> {
syvb marked this conversation as resolved.
Show resolved Hide resolved
let mut bytes = Vec::new();
bytes.try_reserve(size.bytes_usize()).map_err(|_| {
// This results in an error that can happen non-deterministically, since the memory
// available to the compiler can change between runs. Normally queries are always
// deterministic. However, we can be non-determinstic here because all uses of const
// evaluation (including ConstProp!) will make compilation fail (via hard error
// or ICE) upon encountering a `MemoryExhausted` error.
syvb marked this conversation as resolved.
Show resolved Hide resolved
ty::tls::with(|tcx| {
tcx.sess.delay_span_bug(DUMMY_SP, "exhausted memory during interpreation")
});
InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted)
syvb marked this conversation as resolved.
Show resolved Hide resolved
})?;
bytes.resize(size.bytes_usize(), 0);
Ok(Allocation {
bytes,
relocations: Relocations::new(),
init_mask: InitMask::new(size, false),
align,
mutability: Mutability::Mut,
extra: (),
}
})
}
}

Expand Down
8 changes: 7 additions & 1 deletion compiler/rustc_middle/src/mir/interpret/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,8 @@ pub enum ResourceExhaustionInfo {
///
/// The exact limit is set by the `const_eval_limit` attribute.
StepLimitReached,
/// There is not enough memory to perform an allocation.
MemoryExhausted,
}

impl fmt::Display for ResourceExhaustionInfo {
Expand All @@ -435,6 +437,9 @@ impl fmt::Display for ResourceExhaustionInfo {
StepLimitReached => {
write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)")
}
MemoryExhausted => {
write!(f, "tried to allocate more memory than available to compiler")
}
}
}
}
Expand Down Expand Up @@ -525,7 +530,8 @@ impl InterpError<'_> {
use InterpError::*;
match *self {
MachineStop(ref err) => err.is_hard_err(),
InterpError::UndefinedBehavior(_) => true,
UndefinedBehavior(_) => true,
ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) => true,
_ => false,
}
}
Expand Down
10 changes: 5 additions & 5 deletions compiler/rustc_middle/src/ty/vtable.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::convert::TryFrom;

use crate::mir::interpret::{alloc_range, AllocId, Allocation, Pointer, Scalar};
use crate::mir::interpret::{alloc_range, AllocId, Allocation, InterpResult, Pointer, Scalar};
use crate::ty::fold::TypeFoldable;
use crate::ty::{self, DefId, SubstsRef, Ty, TyCtxt};
use rustc_ast::Mutability;
Expand Down Expand Up @@ -28,11 +28,11 @@ impl<'tcx> TyCtxt<'tcx> {
self,
ty: Ty<'tcx>,
poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
) -> AllocId {
) -> InterpResult<'tcx, AllocId> {
let tcx = self;
let vtables_cache = tcx.vtables_cache.lock();
if let Some(alloc_id) = vtables_cache.get(&(ty, poly_trait_ref)).cloned() {
return alloc_id;
return Ok(alloc_id);
}
drop(vtables_cache);

Expand Down Expand Up @@ -60,7 +60,7 @@ impl<'tcx> TyCtxt<'tcx> {
let ptr_align = tcx.data_layout.pointer_align.abi;

let vtable_size = ptr_size * u64::try_from(vtable_entries.len()).unwrap();
let mut vtable = Allocation::uninit(vtable_size, ptr_align);
let mut vtable = Allocation::uninit(vtable_size, ptr_align)?;

// No need to do any alignment checks on the memory accesses below, because we know the
// allocation is correctly aligned as we created it above. Also we're only offsetting by
Expand Down Expand Up @@ -101,6 +101,6 @@ impl<'tcx> TyCtxt<'tcx> {
let alloc_id = tcx.create_memory_alloc(tcx.intern_const_alloc(vtable));
let mut vtables_cache = self.vtables_cache.lock();
vtables_cache.insert((ty, poly_trait_ref), alloc_id);
alloc_id
Ok(alloc_id)
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/const_eval/eval_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
);
let layout = ecx.layout_of(body.return_ty().subst(tcx, cid.instance.substs))?;
assert!(!layout.is_unsized());
let ret = ecx.allocate(layout, MemoryKind::Stack);
let ret = ecx.allocate(layout, MemoryKind::Stack)?;

let name =
with_no_trimmed_paths(|| ty::tls::with(|tcx| tcx.def_path_str(cid.instance.def_id())));
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/const_eval/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
Size::from_bytes(size as u64),
align,
interpret::MemoryKind::Machine(MemoryKind::Heap),
);
)?;
ecx.write_scalar(Scalar::Ptr(ptr), dest)?;
}
_ => {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/interpret/intern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
&MPlaceTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx, ()>,
) -> InterpResult<'tcx, &'tcx Allocation> {
let dest = self.allocate(layout, MemoryKind::Stack);
let dest = self.allocate(layout, MemoryKind::Stack)?;
f(self, &dest)?;
let ptr = dest.ptr.assert_ptr();
assert_eq!(ptr.offset, Size::ZERO);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
.type_of(self.tcx.require_lang_item(LangItem::PanicLocation, None))
.subst(*self.tcx, self.tcx.mk_substs([self.tcx.lifetimes.re_erased.into()].iter()));
let loc_layout = self.layout_of(loc_ty).unwrap();
let location = self.allocate(loc_layout, MemoryKind::CallerLocation);
// This can fail if rustc runs out of memory right here. Trying to emit an error would be
// pointless, since that would require allocating more memory than a Location.
let location = self.allocate(loc_layout, MemoryKind::CallerLocation).unwrap();

// Initialize fields.
self.write_immediate(file.to_ref(), &self.mplace_field(&location, 0).unwrap().into())
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_mir/src/interpret/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
size: Size,
align: Align,
kind: MemoryKind<M::MemoryKind>,
) -> Pointer<M::PointerTag> {
let alloc = Allocation::uninit(size, align);
self.allocate_with(alloc, kind)
) -> InterpResult<'static, Pointer<M::PointerTag>> {
let alloc = Allocation::uninit(size, align)?;
Ok(self.allocate_with(alloc, kind))
}

pub fn allocate_bytes(
Expand Down Expand Up @@ -257,7 +257,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {

// For simplicities' sake, we implement reallocate as "alloc, copy, dealloc".
// This happens so rarely, the perf advantage is outweighed by the maintenance cost.
let new_ptr = self.allocate(new_size, new_align, kind);
let new_ptr = self.allocate(new_size, new_align, kind)?;
let old_size = match old_size_and_align {
Some((size, _align)) => size,
None => self.get_raw(ptr.alloc_id)?.size(),
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_mir/src/interpret/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -982,7 +982,7 @@ where
let (size, align) = self
.size_and_align_of(&meta, &local_layout)?
.expect("Cannot allocate for non-dyn-sized type");
let ptr = self.memory.allocate(size, align, MemoryKind::Stack);
let ptr = self.memory.allocate(size, align, MemoryKind::Stack)?;
let mplace = MemPlace { ptr: ptr.into(), align, meta };
if let LocalValue::Live(Operand::Immediate(value)) = local_val {
// Preserve old value.
Expand Down Expand Up @@ -1018,9 +1018,9 @@ where
&mut self,
layout: TyAndLayout<'tcx>,
kind: MemoryKind<M::MemoryKind>,
) -> MPlaceTy<'tcx, M::PointerTag> {
let ptr = self.memory.allocate(layout.size, layout.align.abi, kind);
MPlaceTy::from_aligned_ptr(ptr, layout)
) -> InterpResult<'static, MPlaceTy<'tcx, M::PointerTag>> {
let ptr = self.memory.allocate(layout.size, layout.align.abi, kind)?;
Ok(MPlaceTy::from_aligned_ptr(ptr, layout))
}

/// Returns a wide MPlace of type `&'static [mut] str` to a new 1-aligned allocation.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/interpret/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
ensure_monomorphic_enough(*self.tcx, ty)?;
ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?;

let vtable_allocation = self.tcx.vtable_allocation(ty, poly_trait_ref);
let vtable_allocation = self.tcx.vtable_allocation(ty, poly_trait_ref)?;

let vtable_ptr = self.memory.global_base_pointer(Pointer::from(vtable_allocation))?;

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Rust MIR: a lowered representation of Rust.
#![feature(option_get_or_insert_default)]
#![feature(once_cell)]
#![feature(control_flow_enum)]
#![feature(try_reserve)]
#![recursion_limit = "256"]

#[macro_use]
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_mir/src/transform/const_prop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
.filter(|ret_layout| {
!ret_layout.is_zst() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT)
})
.map(|ret_layout| ecx.allocate(ret_layout, MemoryKind::Stack).into());
.map(|ret_layout| {
ecx.allocate(ret_layout, MemoryKind::Stack)
.expect("couldn't perform small allocation")
.into()
});

ecx.push_stack_frame(
Instance::new(def_id, substs),
Expand Down
18 changes: 18 additions & 0 deletions src/test/ui/consts/large_const_alloc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// only-64bit
// on 32bit and 16bit platforms it is plausible that the maximum allocation size will succeed

const FOO: () = {
// 128 TiB, unlikely anyone has that much RAM
let x = [0_u8; (1 << 47) - 1];
//~^ ERROR evaluation of constant value failed
};

static FOO2: () = {
let x = [0_u8; (1 << 47) - 1];
//~^ ERROR could not evaluate static initializer
};

fn main() {
let _ = FOO;
let _ = FOO2;
}
15 changes: 15 additions & 0 deletions src/test/ui/consts/large_const_alloc.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error[E0080]: evaluation of constant value failed
--> $DIR/large_const_alloc.rs:6:13
|
LL | let x = [0_u8; (1 << 47) - 1];
| ^^^^^^^^^^^^^^^^^^^^^ tried to allocate more memory than available to compiler

error[E0080]: could not evaluate static initializer
--> $DIR/large_const_alloc.rs:11:13
|
LL | let x = [0_u8; (1 << 47) - 1];
| ^^^^^^^^^^^^^^^^^^^^^ tried to allocate more memory than available to compiler

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0080`.