]> git.lizzy.rs Git - rust.git/commitdiff
std: Mark allocation functions as nounwind
authorAlex Crichton <alex@alexcrichton.com>
Tue, 22 Aug 2017 21:36:49 +0000 (14:36 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Mon, 28 Aug 2017 15:06:52 +0000 (08:06 -0700)
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

src/liballoc/allocator.rs
src/liballoc/heap.rs
src/liballoc/lib.rs
src/librustc_trans/attributes.rs
src/test/codegen/dealloc-no-unwind.rs [new file with mode: 0644]

index fc6585a9f951d6d58c6bb29dbe27ee5eed5eb1b4..c71770290119a69f9204ed79d256a6687bb4b6ea 100644 (file)
@@ -464,6 +464,29 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 ///  * if a layout `k` fits a memory block (denoted by `ptr`)
 ///    currently allocated via an allocator `a`, then it is legal to
 ///    use that layout to deallocate it, i.e. `a.dealloc(ptr, k);`.
+///
+/// # Unsafety
+///
+/// The `Alloc` trait is an `unsafe` trait for a number of reasons, and
+/// implementors must ensure that they adhere to these contracts:
+///
+/// * Pointers returned from allocation functions must point to valid memory and
+///   retain their validity until at least the instance of `Alloc` is dropped
+///   itself.
+///
+/// * It's undefined behavior if global allocators unwind.  This restriction may
+///   be lifted in the future, but currently a panic from any of these
+///   functions may lead to memory unsafety. Note that as of the time of this
+///   writing allocators *not* intending to be global allocators can still panic
+///   in their implementation without violating memory safety.
+///
+/// * `Layout` queries and calculations in general must be correct. Callers of
+///   this trait are allowed to rely on the contracts defined on each method,
+///   and implementors must ensure such contracts remain true.
+///
+/// Note that this list may get tweaked over time as clarifications are made in
+/// the future. Additionally global allocators may gain unique requirements for
+/// how to safely implement one in the future as well.
 pub unsafe trait Alloc {
 
     // (Note: existing allocators have unspecified but well-defined
index 820f2d958d9a8fb066bf1bb8598e71f4356cca2d..b2bd9d7d8fafadf9006240a698d0609f0f6a0bd5 100644 (file)
@@ -27,24 +27,32 @@ pub mod __core {
 
 extern "Rust" {
     #[allocator]
+    #[rustc_allocator_nounwind]
     fn __rust_alloc(size: usize, align: usize, err: *mut u8) -> *mut u8;
     #[cold]
+    #[rustc_allocator_nounwind]
     fn __rust_oom(err: *const u8) -> !;
+    #[rustc_allocator_nounwind]
     fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize);
+    #[rustc_allocator_nounwind]
     fn __rust_usable_size(layout: *const u8,
                           min: *mut usize,
                           max: *mut usize);
+    #[rustc_allocator_nounwind]
     fn __rust_realloc(ptr: *mut u8,
                       old_size: usize,
                       old_align: usize,
                       new_size: usize,
                       new_align: usize,
                       err: *mut u8) -> *mut u8;
+    #[rustc_allocator_nounwind]
     fn __rust_alloc_zeroed(size: usize, align: usize, err: *mut u8) -> *mut u8;
+    #[rustc_allocator_nounwind]
     fn __rust_alloc_excess(size: usize,
                            align: usize,
                            excess: *mut usize,
                            err: *mut u8) -> *mut u8;
+    #[rustc_allocator_nounwind]
     fn __rust_realloc_excess(ptr: *mut u8,
                              old_size: usize,
                              old_align: usize,
@@ -52,11 +60,13 @@ fn __rust_realloc_excess(ptr: *mut u8,
                              new_align: usize,
                              excess: *mut usize,
                              err: *mut u8) -> *mut u8;
+    #[rustc_allocator_nounwind]
     fn __rust_grow_in_place(ptr: *mut u8,
                             old_size: usize,
                             old_align: usize,
                             new_size: usize,
                             new_align: usize) -> u8;
+    #[rustc_allocator_nounwind]
     fn __rust_shrink_in_place(ptr: *mut u8,
                               old_size: usize,
                               old_align: usize,
index 227fcfabcf11de25056fbdf7918a7ae004853dcb..66928e9a480ed51e2e436fb990bfd27a990518e3 100644 (file)
 #![feature(pattern)]
 #![feature(placement_in_syntax)]
 #![feature(placement_new_protocol)]
+#![feature(rustc_attrs)]
 #![feature(shared)]
 #![feature(slice_get_slice)]
 #![feature(slice_patterns)]
index 8863d4ea5ea8ace42a98c71ca388f2c68dbc9c0a..b6ca1460a7d0ac9df44378709e3783aaa35f63d8 100644 (file)
@@ -119,6 +119,8 @@ pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRe
                 llvm::AttributePlace::ReturnValue(), llfn);
         } else if attr.check_name("unwind") {
             unwind(llfn, true);
+        } else if attr.check_name("rustc_allocator_nounwind") {
+            unwind(llfn, false);
         }
     }
     if !target_features.is_empty() {
diff --git a/src/test/codegen/dealloc-no-unwind.rs b/src/test/codegen/dealloc-no-unwind.rs
new file mode 100644 (file)
index 0000000..551b66e
--- /dev/null
@@ -0,0 +1,32 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+// no-system-llvm
+// compile-flags: -O
+
+#![crate_type="lib"]
+
+struct A;
+
+impl Drop for A {
+    fn drop(&mut self) {
+        extern { fn foo(); }
+        unsafe { foo(); }
+    }
+}
+
+#[no_mangle]
+pub fn a(a: Box<i32>) {
+    // CHECK-LABEL: define void @a
+    // CHECK: call void @__rust_dealloc
+    // CHECK-NEXT: call void @foo
+    let _a = A;
+    drop(a);
+}