Skip to content

Commit d2d5069

Browse files
committed
Auto merge of #44049 - alexcrichton:nounwind-allocators, r=BurntSushi
std: Mark allocation functions as nounwind This commit flags all allocation-related functions in liballoc as "this can't unwind" which should largely resolve the size-related issues found on #42808. The documentation on the trait was updated with such a restriction (they can't panic) as well as some other words about the relative instability about implementing a bullet-proof allocator. Closes #42808
2 parents bef07b8 + b6f554b commit d2d5069

File tree

5 files changed

+68
-0
lines changed

5 files changed

+68
-0
lines changed

src/liballoc/allocator.rs

+23
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,29 @@ impl fmt::Display for CannotReallocInPlace {
464464
/// * if a layout `k` fits a memory block (denoted by `ptr`)
465465
/// currently allocated via an allocator `a`, then it is legal to
466466
/// use that layout to deallocate it, i.e. `a.dealloc(ptr, k);`.
467+
///
468+
/// # Unsafety
469+
///
470+
/// The `Alloc` trait is an `unsafe` trait for a number of reasons, and
471+
/// implementors must ensure that they adhere to these contracts:
472+
///
473+
/// * Pointers returned from allocation functions must point to valid memory and
474+
/// retain their validity until at least the instance of `Alloc` is dropped
475+
/// itself.
476+
///
477+
/// * It's undefined behavior if global allocators unwind. This restriction may
478+
/// be lifted in the future, but currently a panic from any of these
479+
/// functions may lead to memory unsafety. Note that as of the time of this
480+
/// writing allocators *not* intending to be global allocators can still panic
481+
/// in their implementation without violating memory safety.
482+
///
483+
/// * `Layout` queries and calculations in general must be correct. Callers of
484+
/// this trait are allowed to rely on the contracts defined on each method,
485+
/// and implementors must ensure such contracts remain true.
486+
///
487+
/// Note that this list may get tweaked over time as clarifications are made in
488+
/// the future. Additionally global allocators may gain unique requirements for
489+
/// how to safely implement one in the future as well.
467490
pub unsafe trait Alloc {
468491

469492
// (Note: existing allocators have unspecified but well-defined

src/liballoc/heap.rs

+10
Original file line numberDiff line numberDiff line change
@@ -27,36 +27,46 @@ pub mod __core {
2727

2828
extern "Rust" {
2929
#[allocator]
30+
#[rustc_allocator_nounwind]
3031
fn __rust_alloc(size: usize, align: usize, err: *mut u8) -> *mut u8;
3132
#[cold]
33+
#[rustc_allocator_nounwind]
3234
fn __rust_oom(err: *const u8) -> !;
35+
#[rustc_allocator_nounwind]
3336
fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize);
37+
#[rustc_allocator_nounwind]
3438
fn __rust_usable_size(layout: *const u8,
3539
min: *mut usize,
3640
max: *mut usize);
41+
#[rustc_allocator_nounwind]
3742
fn __rust_realloc(ptr: *mut u8,
3843
old_size: usize,
3944
old_align: usize,
4045
new_size: usize,
4146
new_align: usize,
4247
err: *mut u8) -> *mut u8;
48+
#[rustc_allocator_nounwind]
4349
fn __rust_alloc_zeroed(size: usize, align: usize, err: *mut u8) -> *mut u8;
50+
#[rustc_allocator_nounwind]
4451
fn __rust_alloc_excess(size: usize,
4552
align: usize,
4653
excess: *mut usize,
4754
err: *mut u8) -> *mut u8;
55+
#[rustc_allocator_nounwind]
4856
fn __rust_realloc_excess(ptr: *mut u8,
4957
old_size: usize,
5058
old_align: usize,
5159
new_size: usize,
5260
new_align: usize,
5361
excess: *mut usize,
5462
err: *mut u8) -> *mut u8;
63+
#[rustc_allocator_nounwind]
5564
fn __rust_grow_in_place(ptr: *mut u8,
5665
old_size: usize,
5766
old_align: usize,
5867
new_size: usize,
5968
new_align: usize) -> u8;
69+
#[rustc_allocator_nounwind]
6070
fn __rust_shrink_in_place(ptr: *mut u8,
6171
old_size: usize,
6272
old_align: usize,

src/liballoc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
#![feature(pattern)]
107107
#![feature(placement_in_syntax)]
108108
#![feature(placement_new_protocol)]
109+
#![feature(rustc_attrs)]
109110
#![feature(shared)]
110111
#![feature(slice_get_slice)]
111112
#![feature(slice_patterns)]

src/librustc_trans/attributes.rs

+2
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRe
119119
llvm::AttributePlace::ReturnValue(), llfn);
120120
} else if attr.check_name("unwind") {
121121
unwind(llfn, true);
122+
} else if attr.check_name("rustc_allocator_nounwind") {
123+
unwind(llfn, false);
122124
}
123125
}
124126
if !target_features.is_empty() {

src/test/codegen/dealloc-no-unwind.rs

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
//
11+
// no-system-llvm
12+
// compile-flags: -O
13+
14+
#![crate_type="lib"]
15+
16+
struct A;
17+
18+
impl Drop for A {
19+
fn drop(&mut self) {
20+
extern { fn foo(); }
21+
unsafe { foo(); }
22+
}
23+
}
24+
25+
#[no_mangle]
26+
pub fn a(a: Box<i32>) {
27+
// CHECK-LABEL: define void @a
28+
// CHECK: call void @__rust_dealloc
29+
// CHECK-NEXT: call void @foo
30+
let _a = A;
31+
drop(a);
32+
}

0 commit comments

Comments
 (0)