Skip to content

Commit 8c6c6d7

Browse files
authored
Merge pull request rust-lang#212 from oli-obk/zero_sense_types
Remove the zst allocation
2 parents f10dd41 + 377fcce commit 8c6c6d7

File tree

12 files changed

+102
-58
lines changed

12 files changed

+102
-58
lines changed

src/error.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ pub enum EvalError<'tcx> {
5959
ReallocatedStaticMemory,
6060
DeallocatedStaticMemory,
6161
Layout(layout::LayoutError<'tcx>),
62+
HeapAllocZeroBytes,
63+
HeapAllocNonPowerOfTwoAlignment(u64),
6264
Unreachable,
6365
Panic,
6466
}
@@ -89,7 +91,7 @@ impl<'tcx> Error for EvalError<'tcx> {
8991
EvalError::ReadBytesAsPointer =>
9092
"a memory access tried to interpret some bytes as a pointer",
9193
EvalError::InvalidPointerMath =>
92-
"attempted to do invalid arithmetic on pointers that would leak base addresses, e.g. compating pointers into different allocations",
94+
"attempted to do invalid arithmetic on pointers that would leak base addresses, e.g. comparing pointers into different allocations",
9395
EvalError::ReadUndefBytes =>
9496
"attempted to read undefined bytes",
9597
EvalError::DeadLocal =>
@@ -146,6 +148,10 @@ impl<'tcx> Error for EvalError<'tcx> {
146148
"rustc layout computation failed",
147149
EvalError::UnterminatedCString(_) =>
148150
"attempted to get length of a null terminated string, but no null found before end of allocation",
151+
EvalError::HeapAllocZeroBytes =>
152+
"tried to re-, de- or allocate zero bytes on the heap",
153+
EvalError::HeapAllocNonPowerOfTwoAlignment(_) =>
154+
"tried to re-, de-, or allocate heap memory with alignment that is not a power of two",
149155
EvalError::Unreachable =>
150156
"entered unreachable code",
151157
EvalError::Panic =>

src/eval_context.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -362,15 +362,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
362362
StackPopCleanup::None => {},
363363
StackPopCleanup::Tls(key) => {
364364
// either fetch the next dtor or start new from the beginning, if any are left with a non-null data
365-
if let Some((instance, ptr, key)) = self.memory.fetch_tls_dtor(key).or_else(|| self.memory.fetch_tls_dtor(None)) {
365+
let dtor = match self.memory.fetch_tls_dtor(key)? {
366+
dtor @ Some(_) => dtor,
367+
None => self.memory.fetch_tls_dtor(None)?,
368+
};
369+
if let Some((instance, ptr, key)) = dtor {
366370
trace!("Running TLS dtor {:?} on {:?}", instance, ptr);
367371
// TODO: Potentially, this has to support all the other possible instances? See eval_fn_call in terminator/mod.rs
368372
let mir = self.load_mir(instance.def)?;
369373
self.push_stack_frame(
370374
instance,
371375
mir.span,
372376
mir,
373-
Lvalue::zst(),
377+
Lvalue::undef(),
374378
StackPopCleanup::Tls(Some(key)),
375379
)?;
376380
let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()))?;
@@ -673,8 +677,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
673677
}
674678

675679
NullaryOp(mir::NullOp::Box, ty) => {
676-
let ptr = self.alloc_ptr(ty)?;
677-
self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
680+
// FIXME: call the `exchange_malloc` lang item if available
681+
if self.type_size(ty)?.expect("box only works with sized types") == 0 {
682+
let align = self.type_align(ty)?;
683+
self.write_primval(dest, PrimVal::Bytes(align.into()), dest_ty)?;
684+
} else {
685+
let ptr = self.alloc_ptr(ty)?;
686+
self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
687+
}
678688
}
679689

680690
NullaryOp(mir::NullOp::SizeOf, ty) => {
@@ -904,11 +914,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
904914
let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64;
905915
return if let Some(offset) = offset.checked_mul(pointee_size) {
906916
let ptr = ptr.signed_offset(offset, self.memory.layout)?;
907-
// Do not do bounds-checking for integers or ZST; they can never alias a normal pointer anyway.
917+
// Do not do bounds-checking for integers; they can never alias a normal pointer anyway.
908918
if let PrimVal::Ptr(ptr) = ptr {
909-
if !(ptr.points_to_zst() && (offset == 0 || pointee_size == 0)) {
910-
self.memory.check_bounds(ptr, false)?;
911-
}
919+
self.memory.check_bounds(ptr, false)?;
912920
} else if ptr.is_null()? {
913921
// We moved *to* a NULL pointer. That seems wrong, LLVM considers the NULL pointer its own small allocation. Reject this, for now.
914922
return Err(EvalError::NullPointerOutOfBounds);
@@ -1697,7 +1705,7 @@ pub fn eval_main<'a, 'tcx: 'a>(
16971705
main_instance,
16981706
main_mir.span,
16991707
main_mir,
1700-
Lvalue::zst(),
1708+
Lvalue::undef(),
17011709
StackPopCleanup::Tls(None),
17021710
)?;
17031711
}

src/lvalue.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use eval_context::{EvalContext};
88
use memory::Pointer;
99
use value::{PrimVal, Value};
1010

11-
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
11+
#[derive(Copy, Clone, Debug)]
1212
pub enum Lvalue<'tcx> {
1313
/// An lvalue referring to a value allocated in the `Memory` system.
1414
Ptr {
@@ -73,10 +73,6 @@ impl<'tcx> Lvalue<'tcx> {
7373
Lvalue::Ptr { ptr, extra: LvalueExtra::None }
7474
}
7575

76-
pub fn zst() -> Self {
77-
Self::from_ptr(Pointer::zst_ptr())
78-
}
79-
8076
pub fn from_ptr(ptr: Pointer) -> Self {
8177
Self::from_primval_ptr(PrimVal::Ptr(ptr))
8278
}

src/memory.rs

Lines changed: 11 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,6 @@ impl Pointer {
8181
pub fn offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> {
8282
Ok(Pointer::new(self.alloc_id, value::offset(self.offset, i, layout)?))
8383
}
84-
85-
pub fn points_to_zst(&self) -> bool {
86-
self.alloc_id == ZST_ALLOC_ID
87-
}
88-
89-
pub fn zst_ptr() -> Self {
90-
Pointer::new(ZST_ALLOC_ID, 0)
91-
}
9284
}
9385

9486
pub type TlsKey = usize;
@@ -157,15 +149,13 @@ pub struct Memory<'a, 'tcx> {
157149
next_thread_local: TlsKey,
158150
}
159151

160-
const ZST_ALLOC_ID: AllocId = AllocId(0);
161-
162152
impl<'a, 'tcx> Memory<'a, 'tcx> {
163153
pub fn new(layout: &'a TargetDataLayout, max_memory: u64) -> Self {
164154
Memory {
165155
alloc_map: HashMap::new(),
166156
functions: HashMap::new(),
167157
function_alloc_cache: HashMap::new(),
168-
next_id: AllocId(2),
158+
next_id: AllocId(0),
169159
layout,
170160
memory_size: max_memory,
171161
memory_usage: 0,
@@ -206,10 +196,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
206196
}
207197

208198
pub fn allocate(&mut self, size: u64, align: u64) -> EvalResult<'tcx, Pointer> {
209-
if size == 0 {
210-
return Ok(Pointer::zst_ptr());
211-
}
212199
assert_ne!(align, 0);
200+
assert!(align.is_power_of_two());
213201

214202
if self.memory_size - self.memory_usage < size {
215203
return Err(EvalError::OutOfMemory {
@@ -236,13 +224,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
236224
// TODO(solson): Track which allocations were returned from __rust_allocate and report an error
237225
// when reallocating/deallocating any others.
238226
pub fn reallocate(&mut self, ptr: Pointer, new_size: u64, align: u64) -> EvalResult<'tcx, Pointer> {
227+
assert!(align.is_power_of_two());
239228
// TODO(solson): Report error about non-__rust_allocate'd pointer.
240229
if ptr.offset != 0 {
241230
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
242231
}
243-
if ptr.points_to_zst() {
244-
return self.allocate(new_size, align);
245-
}
246232
if self.get(ptr.alloc_id).ok().map_or(false, |alloc| alloc.static_kind != StaticKind::NotStatic) {
247233
return Err(EvalError::ReallocatedStaticMemory);
248234
}
@@ -253,13 +239,15 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
253239
let amount = new_size - size;
254240
self.memory_usage += amount;
255241
let alloc = self.get_mut(ptr.alloc_id)?;
242+
// FIXME: check alignment here
256243
assert_eq!(amount as usize as u64, amount);
257244
alloc.bytes.extend(iter::repeat(0).take(amount as usize));
258245
alloc.undef_mask.grow(amount, false);
259246
} else if size > new_size {
260247
self.memory_usage -= size - new_size;
261248
self.clear_relocations(ptr.offset(new_size, self.layout)?, size - new_size)?;
262249
let alloc = self.get_mut(ptr.alloc_id)?;
250+
// FIXME: check alignment here
263251
// `as usize` is fine here, since it is smaller than `size`, which came from a usize
264252
alloc.bytes.truncate(new_size as usize);
265253
alloc.bytes.shrink_to_fit();
@@ -271,9 +259,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
271259

272260
// TODO(solson): See comment on `reallocate`.
273261
pub fn deallocate(&mut self, ptr: Pointer) -> EvalResult<'tcx> {
274-
if ptr.points_to_zst() {
275-
return Ok(());
276-
}
277262
if ptr.offset != 0 {
278263
// TODO(solson): Report error about non-__rust_allocate'd pointer.
279264
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
@@ -419,22 +404,22 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
419404
/// with associated destructors, implementations may stop calling destructors,
420405
/// or they may continue calling destructors until no non-NULL values with
421406
/// associated destructors exist, even though this might result in an infinite loop.
422-
pub(crate) fn fetch_tls_dtor(&mut self, key: Option<TlsKey>) -> Option<(ty::Instance<'tcx>, PrimVal, TlsKey)> {
407+
pub(crate) fn fetch_tls_dtor(&mut self, key: Option<TlsKey>) -> EvalResult<'tcx, Option<(ty::Instance<'tcx>, PrimVal, TlsKey)>> {
423408
use std::collections::Bound::*;
424409
let start = match key {
425410
Some(key) => Excluded(key),
426411
None => Unbounded,
427412
};
428413
for (&key, &mut TlsEntry { ref mut data, dtor }) in self.thread_local.range_mut((start, Unbounded)) {
429-
if *data != PrimVal::Bytes(0) {
414+
if !data.is_null()? {
430415
if let Some(dtor) = dtor {
431416
let ret = Some((dtor, *data, key));
432417
*data = PrimVal::Bytes(0);
433-
return ret;
418+
return Ok(ret);
434419
}
435420
}
436421
}
437-
return None;
422+
return Ok(None);
438423
}
439424
}
440425

@@ -459,7 +444,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
459444
Some(alloc) => Ok(alloc),
460445
None => match self.functions.get(&id) {
461446
Some(_) => Err(EvalError::DerefFunctionPointer),
462-
None if id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess),
463447
None => Err(EvalError::DanglingPointerDeref),
464448
}
465449
}
@@ -474,7 +458,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
474458
},
475459
None => match self.functions.get(&id) {
476460
Some(_) => Err(EvalError::DerefFunctionPointer),
477-
None if id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess),
478461
None => Err(EvalError::DanglingPointerDeref),
479462
}
480463
}
@@ -508,7 +491,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
508491
let mut allocs_seen = HashSet::new();
509492

510493
while let Some(id) = allocs_to_print.pop_front() {
511-
if id == ZST_ALLOC_ID { continue; }
512494
let mut msg = format!("Alloc {:<5} ", format!("{}:", id));
513495
let prefix_len = msg.len();
514496
let mut relocations = vec![];
@@ -556,10 +538,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
556538
for (i, target_id) in relocations {
557539
// this `as usize` is fine, since we can't print more chars than `usize::MAX`
558540
write!(msg, "{:1$}", "", ((i - pos) * 3) as usize).unwrap();
559-
let target = match target_id {
560-
ZST_ALLOC_ID => String::from("zst"),
561-
_ => format!("({})", target_id),
562-
};
541+
let target = format!("({})", target_id);
563542
// this `as usize` is fine, since we can't print more chars than `usize::MAX`
564543
write!(msg, "└{0:─^1$}┘ ", target, relocation_width as usize).unwrap();
565544
pos = i + self.pointer_size();
@@ -637,7 +616,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
637616
/// mark an allocation as being the entry point to a static (see `static_alloc` field)
638617
pub fn mark_static(&mut self, alloc_id: AllocId) {
639618
trace!("mark_static: {:?}", alloc_id);
640-
if alloc_id != ZST_ALLOC_ID && !self.static_alloc.insert(alloc_id) {
619+
if !self.static_alloc.insert(alloc_id) {
641620
bug!("tried to mark an allocation ({:?}) as static twice", alloc_id);
642621
}
643622
}
@@ -667,7 +646,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
667646
// mark recursively
668647
mem::replace(relocations, Default::default())
669648
},
670-
None if alloc_id == ZST_ALLOC_ID => return Ok(()),
671649
None if !self.functions.contains_key(&alloc_id) => return Err(EvalError::DanglingPointerDeref),
672650
_ => return Ok(()),
673651
};

src/operator.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
159159
},
160160
// These work on anything
161161
Eq if left_kind == right_kind => {
162-
return Ok((PrimVal::from_bool(left == right), false));
162+
let result = match (left, right) {
163+
(PrimVal::Bytes(left), PrimVal::Bytes(right)) => left == right,
164+
(PrimVal::Ptr(left), PrimVal::Ptr(right)) => left == right,
165+
(PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes),
166+
_ => false,
167+
};
168+
return Ok((PrimVal::from_bool(result), false));
163169
}
164170
Ne if left_kind == right_kind => {
165-
return Ok((PrimVal::from_bool(left != right), false));
171+
let result = match (left, right) {
172+
(PrimVal::Bytes(left), PrimVal::Bytes(right)) => left != right,
173+
(PrimVal::Ptr(left), PrimVal::Ptr(right)) => left != right,
174+
(PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes),
175+
_ => true,
176+
};
177+
return Ok((PrimVal::from_bool(result), false));
166178
}
167179
// These need both pointers to be in the same allocation
168180
Lt | Le | Gt | Ge | Sub

src/terminator/drop.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
4949
instance,
5050
span,
5151
mir,
52-
Lvalue::zst(),
52+
Lvalue::undef(),
5353
StackPopCleanup::None,
5454
)?;
5555

src/terminator/mod.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -596,13 +596,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
596596
"__rust_allocate" => {
597597
let size = self.value_to_primval(args[0], usize)?.to_u64()?;
598598
let align = self.value_to_primval(args[1], usize)?.to_u64()?;
599+
if size == 0 {
600+
return Err(EvalError::HeapAllocZeroBytes);
601+
}
602+
if !align.is_power_of_two() {
603+
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align));
604+
}
599605
let ptr = self.memory.allocate(size, align)?;
600606
self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
601607
}
602608

603609
"__rust_allocate_zeroed" => {
604610
let size = self.value_to_primval(args[0], usize)?.to_u64()?;
605611
let align = self.value_to_primval(args[1], usize)?.to_u64()?;
612+
if size == 0 {
613+
return Err(EvalError::HeapAllocZeroBytes);
614+
}
615+
if !align.is_power_of_two() {
616+
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align));
617+
}
606618
let ptr = self.memory.allocate(size, align)?;
607619
self.memory.write_repeat(ptr, 0, size)?;
608620
self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
@@ -611,15 +623,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
611623
"__rust_deallocate" => {
612624
let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?;
613625
// FIXME: insert sanity check for size and align?
614-
let _old_size = self.value_to_primval(args[1], usize)?.to_u64()?;
615-
let _align = self.value_to_primval(args[2], usize)?.to_u64()?;
626+
let old_size = self.value_to_primval(args[1], usize)?.to_u64()?;
627+
let align = self.value_to_primval(args[2], usize)?.to_u64()?;
628+
if old_size == 0 {
629+
return Err(EvalError::HeapAllocZeroBytes);
630+
}
631+
if !align.is_power_of_two() {
632+
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align));
633+
}
616634
self.memory.deallocate(ptr)?;
617635
},
618636

619637
"__rust_reallocate" => {
620638
let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?;
621639
let size = self.value_to_primval(args[2], usize)?.to_u64()?;
622640
let align = self.value_to_primval(args[3], usize)?.to_u64()?;
641+
if size == 0 {
642+
return Err(EvalError::HeapAllocZeroBytes);
643+
}
644+
if !align.is_power_of_two() {
645+
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align));
646+
}
623647
let new_ptr = self.memory.reallocate(ptr, size, align)?;
624648
self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?;
625649
}
@@ -640,7 +664,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
640664
f_instance,
641665
mir.span,
642666
mir,
643-
Lvalue::zst(),
667+
Lvalue::undef(),
644668
StackPopCleanup::Goto(dest_block),
645669
)?;
646670

src/value.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub enum Value {
4242
/// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in
4343
/// size. Like a range of bytes in an `Allocation`, a `PrimVal` can either represent the raw bytes
4444
/// of a simple value, a pointer into another `Allocation`, or be undefined.
45-
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
45+
#[derive(Clone, Copy, Debug)]
4646
pub enum PrimVal {
4747
/// The raw bytes of a simple value.
4848
Bytes(u128),

tests/compile-fail/zst.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
fn main() {
22
let x = &() as *const () as *const i32;
3-
let _ = unsafe { *x }; //~ ERROR: tried to access memory through an invalid pointer
3+
let _ = unsafe { *x }; //~ ERROR: tried to access memory with alignment 1, but alignment 4 is required
44
}

0 commit comments

Comments
 (0)